AssetServerHandler.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  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 <native/utilities/AssetServerHandler.h>
  9. #include <native/resourcecompiler/rcjob.h>
  10. #include <AzCore/Serialization/Json/JsonUtils.h>
  11. #include <AzToolsFramework/Archive/ArchiveAPI.h>
  12. #include <AzCore/JSON/pointer.h>
  13. #include <QDir>
  14. namespace AssetProcessor
  15. {
  16. void CleanupFilename(QString& string)
  17. {
  18. AZStd::string buffer(string.toUtf8().toStdString().c_str());
  19. AZStd::string forbiddenChars("\\/:?\"<>|");
  20. AZStd::replace_if(
  21. buffer.begin(),
  22. buffer.end(),
  23. [forbiddenChars](char c) { return AZStd::string::npos != forbiddenChars.find(c); },
  24. ' ');
  25. string.clear();
  26. string.append(buffer.c_str());
  27. }
  28. AssetServerMode CheckServerMode()
  29. {
  30. AssetServerMode enableCacheServerMode = AssetServerMode::Inactive;
  31. auto settingsRegistry = AZ::SettingsRegistry::Get();
  32. if (settingsRegistry)
  33. {
  34. bool enableAssetCacheServerMode = false;
  35. AZ::SettingsRegistryInterface::FixedValueString key(AssetProcessor::AssetProcessorServerKey);
  36. key += "/";
  37. if (settingsRegistry->Get(enableAssetCacheServerMode, key + "enableCacheServer"))
  38. {
  39. enableCacheServerMode = enableAssetCacheServerMode ? AssetServerMode::Server : AssetServerMode::Client;
  40. AZ_Warning(AssetProcessor::DebugChannel, false, "The 'enableCacheServer' key is deprecated. Please swith to 'assetCacheServerMode'");
  41. }
  42. AZStd::string assetCacheServerModeValue;
  43. if (settingsRegistry->Get(assetCacheServerModeValue, key + AssetCacheServerModeKey))
  44. {
  45. AZStd::to_lower(assetCacheServerModeValue.begin(), assetCacheServerModeValue.end());
  46. if(assetCacheServerModeValue == "server")
  47. {
  48. return AssetServerMode::Server;
  49. }
  50. else if (assetCacheServerModeValue == "client")
  51. {
  52. return AssetServerMode::Client;
  53. }
  54. else if (assetCacheServerModeValue != "inactive")
  55. {
  56. AZ_Warning(AssetProcessor::DebugChannel, false, "Unknown mode for 'assetCacheServerMode' (%s)", assetCacheServerModeValue.c_str());
  57. }
  58. }
  59. }
  60. return enableCacheServerMode;
  61. }
  62. AZStd::string CheckServerAddress()
  63. {
  64. auto settingsRegistry = AZ::SettingsRegistry::Get();
  65. if (settingsRegistry)
  66. {
  67. AZStd::string address;
  68. if (settingsRegistry->Get(address,
  69. AZ::SettingsRegistryInterface::FixedValueString(AssetProcessor::AssetProcessorServerKey)
  70. + "/"
  71. + CacheServerAddressKey))
  72. {
  73. AZ_TracePrintf(AssetProcessor::DebugChannel, "Server Address: %s\n", address.c_str());
  74. return AZStd::move(address);
  75. }
  76. }
  77. return {};
  78. }
  79. QString AssetServerHandler::ComputeArchiveFilePath(const AssetProcessor::BuilderParams& builderParams)
  80. {
  81. QFileInfo fileInfo(builderParams.m_processJobRequest.m_sourceFile.c_str());
  82. QString assetServerAddress = QDir::toNativeSeparators(QString{m_serverAddress.c_str()});
  83. if (!assetServerAddress.isEmpty())
  84. {
  85. QString archiveFileName = builderParams.GetServerKey() + ".zip";
  86. CleanupFilename(archiveFileName);
  87. QDir archiveFolder = QDir(assetServerAddress).filePath(fileInfo.path());
  88. QString archiveFilePath = archiveFolder.filePath(archiveFileName);
  89. // create directories if does not exists
  90. if (!archiveFolder.exists())
  91. {
  92. archiveFolder.mkdir(".");
  93. }
  94. return archiveFilePath;
  95. }
  96. else
  97. {
  98. AZStd::string filePath;
  99. AssetServerInfoBus::BroadcastResult(filePath, &AssetServerInfoBus::Events::ComputeArchiveFilePath, builderParams);
  100. if (!filePath.empty())
  101. {
  102. QString archiveFilePath(filePath.c_str());
  103. return archiveFilePath;
  104. }
  105. }
  106. return QString();
  107. }
  108. const char* AssetServerHandler::GetAssetServerModeText(AssetServerMode mode)
  109. {
  110. switch (mode)
  111. {
  112. case AssetServerMode::Inactive: return "inactive";
  113. case AssetServerMode::Server: return "server";
  114. case AssetServerMode::Client: return "client";
  115. default:
  116. break;
  117. }
  118. return "unknown";
  119. }
  120. AssetServerHandler::AssetServerHandler()
  121. {
  122. SetRemoteCachingMode(CheckServerMode());
  123. SetServerAddress(CheckServerAddress());
  124. AssetServerBus::Handler::BusConnect();
  125. }
  126. AssetServerHandler::~AssetServerHandler()
  127. {
  128. SetRemoteCachingMode(AssetServerMode::Inactive);
  129. AssetServerBus::Handler::BusDisconnect();
  130. }
  131. bool AssetServerHandler::IsServerAddressValid()
  132. {
  133. QString address{m_serverAddress.c_str()};
  134. bool isValid = !address.isEmpty() && QDir(address).exists();
  135. return isValid;
  136. }
  137. void AssetServerHandler::HandleRemoteConfiguration()
  138. {
  139. if (m_assetCachingMode == AssetServerMode::Inactive || !IsServerAddressValid())
  140. {
  141. return;
  142. }
  143. AZ::IO::Path settingsFilePath{ m_serverAddress };
  144. settingsFilePath /= "settings.json";
  145. auto* recognizerConfiguration = AZ::Interface<AssetProcessor::RecognizerConfiguration>::Get();
  146. if (!recognizerConfiguration)
  147. {
  148. return;
  149. }
  150. if (m_assetCachingMode == AssetServerMode::Server)
  151. {
  152. AZStd::string jsonBuffer;
  153. const auto& assetCacheRecognizerContainer = recognizerConfiguration->GetAssetCacheRecognizerContainer();
  154. AssetProcessor::PlatformConfiguration::ConvertToJson(assetCacheRecognizerContainer, jsonBuffer);
  155. if (jsonBuffer.empty())
  156. {
  157. // no configuration to save
  158. return;
  159. }
  160. // save the configuration
  161. rapidjson::Document recognizerDoc;
  162. recognizerDoc.Parse(jsonBuffer.c_str());
  163. AZ::JsonSerializationUtils::WriteJsonFile(recognizerDoc, settingsFilePath.LexicallyNormal().c_str());
  164. }
  165. else if (m_assetCachingMode == AssetServerMode::Client)
  166. {
  167. // load the configuration
  168. if (!AZ::IO::SystemFile::Exists(settingsFilePath.c_str()))
  169. {
  170. // no log since it is okay to not have a settings file
  171. return;
  172. }
  173. auto result = AZ::JsonSerializationUtils::ReadJsonFile(settingsFilePath.LexicallyNormal().c_str());
  174. if (!result.IsSuccess())
  175. {
  176. AZ_Warning(AssetProcessor::DebugChannel, false, "ACS settings file failed with (%s)", result.GetError().c_str());
  177. return;
  178. }
  179. rapidjson::StringBuffer stringBuffer;
  180. rapidjson::Writer<rapidjson::StringBuffer> writer(stringBuffer);
  181. if (result.GetValue().Accept(writer) == false)
  182. {
  183. AZ_Warning(AssetProcessor::DebugChannel, false, "ACS failed to load settings file (%s)", settingsFilePath.c_str());
  184. return;
  185. }
  186. RecognizerContainer recognizerContainer;
  187. if (!AssetProcessor::PlatformConfiguration::ConvertFromJson(stringBuffer.GetString(), recognizerContainer))
  188. {
  189. AZ_Warning(AssetProcessor::DebugChannel, false, "ACS failed to convert settings file (%s)", settingsFilePath.c_str());
  190. return;
  191. }
  192. recognizerConfiguration->AddAssetCacheRecognizerContainer(recognizerContainer);
  193. }
  194. }
  195. AssetServerMode AssetServerHandler::GetRemoteCachingMode() const
  196. {
  197. return m_assetCachingMode;
  198. }
  199. void AssetServerHandler::SetRemoteCachingMode(AssetServerMode mode)
  200. {
  201. m_assetCachingMode = mode;
  202. AssetServerNotificationBus::Broadcast(&AssetServerNotificationBus::Events::OnRemoteCachingModeChanged, mode);
  203. }
  204. const AZStd::string& AssetServerHandler::GetServerAddress() const
  205. {
  206. return m_serverAddress;
  207. }
  208. bool AssetServerHandler::SetServerAddress(const AZStd::string& address)
  209. {
  210. AZStd::string previousServerAddress = m_serverAddress;
  211. m_serverAddress = address;
  212. if (!IsServerAddressValid())
  213. {
  214. m_serverAddress = previousServerAddress;
  215. AZ_Error(AssetProcessor::DebugChannel,
  216. m_assetCachingMode == AssetServerMode::Inactive,
  217. "Server address (%.*s) is invalid! Reverting back to (%.*s)",
  218. AZ_STRING_ARG(address),
  219. AZ_STRING_ARG(previousServerAddress));
  220. return false;
  221. }
  222. return true;
  223. }
  224. bool AssetServerHandler::RetrieveJobResult(const AssetProcessor::BuilderParams& builderParams)
  225. {
  226. AssetBuilderSDK::JobCancelListener jobCancelListener(builderParams.m_rcJob->GetJobEntry().m_jobRunKey);
  227. AssetUtilities::QuitListener listener;
  228. listener.BusConnect();
  229. QString archiveAbsFilePath = ComputeArchiveFilePath(builderParams);
  230. if (archiveAbsFilePath.isEmpty())
  231. {
  232. AZ_Error(AssetProcessor::DebugChannel, false, "Extracting archive operation failed. Archive Absolute Path is empty.");
  233. return false;
  234. }
  235. if (!QFile::exists(archiveAbsFilePath))
  236. {
  237. // file does not exist on the server
  238. AZ_TracePrintf(AssetProcessor::DebugChannel, "Extracting archive operation canceled. Archive does not exist on server. \n");
  239. return false;
  240. }
  241. if (listener.WasQuitRequested() || jobCancelListener.IsCancelled())
  242. {
  243. AZ_TracePrintf(AssetProcessor::DebugChannel, "Extracting archive operation canceled. \n");
  244. return false;
  245. }
  246. AZ_TracePrintf(AssetProcessor::DebugChannel, "Extracting archive for job (%s, %s, %s) with fingerprint (%u).\n",
  247. builderParams.m_rcJob->GetJobEntry().m_sourceAssetReference.AbsolutePath().c_str(), builderParams.m_rcJob->GetJobKey().toUtf8().data(),
  248. builderParams.m_rcJob->GetPlatformInfo().m_identifier.c_str(), builderParams.m_rcJob->GetOriginalFingerprint());
  249. std::future<bool> extractResult;
  250. AzToolsFramework::ArchiveCommandsBus::BroadcastResult(extractResult,
  251. &AzToolsFramework::ArchiveCommandsBus::Events::ExtractArchive,
  252. archiveAbsFilePath.toUtf8().data(), builderParams.GetTempJobDirectory());
  253. bool success = extractResult.valid() ? extractResult.get() : false;
  254. AZ_Error(AssetProcessor::DebugChannel, success, "Extracting archive operation failed.\n");
  255. return success;
  256. }
  257. bool AssetServerHandler::StoreJobResult(const AssetProcessor::BuilderParams& builderParams, AZStd::vector<AZStd::string>& sourceFileList)
  258. {
  259. AssetBuilderSDK::JobCancelListener jobCancelListener(builderParams.m_rcJob->GetJobEntry().m_jobRunKey);
  260. AssetUtilities::QuitListener listener;
  261. listener.BusConnect();
  262. QString archiveAbsFilePath = ComputeArchiveFilePath(builderParams);
  263. if (archiveAbsFilePath.isEmpty())
  264. {
  265. AZ_Error(AssetProcessor::DebugChannel, false, "Creating archive operation failed. Archive Absolute Path is empty. \n");
  266. return false;
  267. }
  268. if (QFile::exists(archiveAbsFilePath))
  269. {
  270. // file already exists on the server
  271. AZ_TracePrintf(AssetProcessor::DebugChannel, "Creating archive operation canceled. An archive of this asset already exists on server. \n");
  272. return true;
  273. }
  274. if (listener.WasQuitRequested() || jobCancelListener.IsCancelled())
  275. {
  276. AZ_TracePrintf(AssetProcessor::DebugChannel, "Creating archive operation canceled. \n");
  277. return false;
  278. }
  279. // make sub-folders if needed
  280. QFileInfo fileInfo(archiveAbsFilePath);
  281. if (!fileInfo.absoluteDir().exists())
  282. {
  283. if (!fileInfo.absoluteDir().mkpath("."))
  284. {
  285. AZ_Error(AssetProcessor::DebugChannel, false, "Could not make archive folder %s !",
  286. fileInfo.absoluteDir().absolutePath().toUtf8().data());
  287. return false;
  288. }
  289. }
  290. AZ_TracePrintf(AssetProcessor::DebugChannel, "Creating archive for job (%s, %s, %s) with fingerprint (%u).\n",
  291. builderParams.m_rcJob->GetJobEntry().m_sourceAssetReference.AbsolutePath().c_str(), builderParams.m_rcJob->GetJobKey().toUtf8().data(),
  292. builderParams.m_rcJob->GetPlatformInfo().m_identifier.c_str(), builderParams.m_rcJob->GetOriginalFingerprint());
  293. std::future<bool> createResult;
  294. AzToolsFramework::ArchiveCommandsBus::BroadcastResult(createResult,
  295. &AzToolsFramework::ArchiveCommandsBus::Events::CreateArchive,
  296. archiveAbsFilePath.toUtf8().data(), builderParams.GetTempJobDirectory());
  297. bool success = createResult.valid() ? createResult.get() : false;
  298. AZ_Error(AssetProcessor::DebugChannel, success, "Creating archive operation failed. \n");
  299. if (success && sourceFileList.size())
  300. {
  301. // Check if any of our output products for this job was a source file which would not be in the temp folder
  302. // If so add it to the archive
  303. AddSourceFilesToArchive(builderParams, archiveAbsFilePath, sourceFileList);
  304. }
  305. return success;
  306. }
  307. bool AssetServerHandler::AddSourceFilesToArchive(const AssetProcessor::BuilderParams& builderParams, const QString& archivePath, AZStd::vector<AZStd::string>& sourceFileList)
  308. {
  309. bool allSuccess{ true };
  310. for (const auto& thisProduct : sourceFileList)
  311. {
  312. QFileInfo sourceFile{ builderParams.m_rcJob->GetJobEntry().GetAbsoluteSourcePath() };
  313. QDir sourceDir{ sourceFile.absoluteDir() };
  314. if (!QFileInfo(sourceDir.absoluteFilePath(thisProduct.c_str())).exists())
  315. {
  316. AZ_Warning(AssetProcessor::DebugChannel, false, "Failed to add %s to %s - source does not exist in expected location (sourceDir %s )", thisProduct.c_str(), archivePath.toUtf8().data(), sourceDir.path().toUtf8().data());
  317. allSuccess = false;
  318. continue;
  319. }
  320. std::future<bool> addResult;
  321. AzToolsFramework::ArchiveCommandsBus::BroadcastResult(addResult,
  322. &AzToolsFramework::ArchiveCommandsBus::Events::AddFileToArchive,
  323. archivePath.toUtf8().data(), sourceDir.path().toUtf8().data(), thisProduct.c_str());
  324. bool success = addResult.valid() ? addResult.get() : false;
  325. if (!success)
  326. {
  327. AZ_Warning(AssetProcessor::DebugChannel, false, "Failed to add %s to %s", thisProduct.c_str(), archivePath.toUtf8().data());
  328. allSuccess = false;
  329. }
  330. }
  331. return allSuccess;
  332. }
  333. }// AssetProcessor