BundlingSystemComponent.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  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 "BundlingSystemComponent.h"
  9. #include <AzCore/Asset/AssetManagerBus.h>
  10. #include <AzCore/Console/IConsole.h>
  11. #include <AzCore/Interface/Interface.h>
  12. #include <AzCore/Serialization/Utils.h>
  13. #include <AzCore/std/string/string_view.h>
  14. #include <AzCore/StringFunc/StringFunc.h>
  15. #include <AzFramework/Asset/AssetBundleManifest.h>
  16. #include <AzFramework/Archive/IArchive.h>
  17. namespace LmbrCentral
  18. {
  19. const char bundleRoot[] = "@products@";
  20. // Calls the LoadBundles method
  21. static void ConsoleCommandLoadBundles(const AZ::ConsoleCommandContainer& commandArgs);
  22. // Calls the UnloadBundles method
  23. static void ConsoleCommandUnloadBundles(const AZ::ConsoleCommandContainer& commandArgs);
  24. AZ_CONSOLEFREEFUNC("loadbundles", ConsoleCommandLoadBundles, AZ::ConsoleFunctorFlags::Null, "Load Asset Bundles");
  25. AZ_CONSOLEFREEFUNC("unloadbundles", ConsoleCommandUnloadBundles, AZ::ConsoleFunctorFlags::Null, "Unload Asset Bundles");
  26. void BundlingSystemComponent::Activate()
  27. {
  28. BundlingSystemRequestBus::Handler::BusConnect();
  29. AZ::IO::ArchiveNotificationBus::Handler::BusConnect();
  30. }
  31. void BundlingSystemComponent::Deactivate()
  32. {
  33. AZ::IO::ArchiveNotificationBus::Handler::BusDisconnect();
  34. BundlingSystemRequestBus::Handler::BusDisconnect();
  35. }
  36. void BundlingSystemComponent::Reflect(AZ::ReflectContext* context)
  37. {
  38. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  39. {
  40. serializeContext->Class<BundlingSystemComponent, AZ::Component>()
  41. ->Version(1)
  42. ;
  43. }
  44. }
  45. AZStd::vector<AZStd::string> BundlingSystemComponent::GetBundleList(const char* bundlePath, const char* bundleExtension) const
  46. {
  47. AZStd::string fileFilter{ AZStd::string::format("*%s", bundleExtension) };
  48. AZStd::vector<AZStd::string> bundleList;
  49. AZ::IO::FileIOBase::GetInstance()->FindFiles(bundlePath, fileFilter.c_str(), [&bundleList](const char* foundPath) -> bool
  50. {
  51. if (!AZ::IO::FileIOBase::GetInstance()->IsDirectory(foundPath))
  52. {
  53. bundleList.push_back(foundPath);
  54. }
  55. return true;
  56. });
  57. return bundleList;
  58. }
  59. void ConsoleCommandLoadBundles(const AZ::ConsoleCommandContainer& commandArgs)
  60. {
  61. const char defaultBundleFolder[] = "bundles";
  62. const char defaultBundleExtension[] = ".pak";
  63. AZ::CVarFixedString bundleFolder = commandArgs.size() > 0 ? AZ::CVarFixedString(commandArgs[0]) : defaultBundleFolder;
  64. AZ::CVarFixedString bundleExtension = commandArgs.size() > 1 ? AZ::CVarFixedString(commandArgs[1]) : defaultBundleExtension;
  65. BundlingSystemRequestBus::Broadcast(&BundlingSystemRequestBus::Events::LoadBundles, bundleFolder.c_str(), bundleExtension.c_str());
  66. }
  67. void ConsoleCommandUnloadBundles([[maybe_unused]] const AZ::ConsoleCommandContainer& commandArgs)
  68. {
  69. BundlingSystemRequestBus::Broadcast(&BundlingSystemRequestBus::Events::UnloadBundles);
  70. }
  71. void BundlingSystemComponent::UnloadBundles()
  72. {
  73. auto archive = AZ::Interface<AZ::IO::IArchive>::Get();
  74. if (!archive)
  75. {
  76. AZ_Error("BundlingSystem", false, "Couldn't Get IArchive to load bundles!");
  77. return;
  78. }
  79. if (!m_bundleModeBundles.size())
  80. {
  81. AZ_TracePrintf("BundlingSystem", "No bundles currently loaded\n");
  82. return;
  83. }
  84. AZStd::lock_guard<AZStd::mutex> openBundleLock(m_bundleModeMutex);
  85. for (const auto& thisBundle : m_bundleModeBundles)
  86. {
  87. if (archive->ClosePack(thisBundle.c_str()))
  88. {
  89. AZ_TracePrintf("BundlingSystem", "Unloaded %s\n",thisBundle.c_str());
  90. }
  91. else
  92. {
  93. AZ_TracePrintf("BundlingSystem", "Failed to unload %s\n",thisBundle.c_str());
  94. }
  95. }
  96. m_bundleModeBundles.clear();
  97. }
  98. void BundlingSystemComponent::LoadBundles(const char* bundleFolder, const char* bundleExtension)
  99. {
  100. AZStd::vector<AZStd::string> bundleList = GetBundleList(bundleFolder, bundleExtension);
  101. AZ_TracePrintf("BundlingSystem", "Loading bundles from %s of type %s\n",bundleFolder, bundleExtension);
  102. if (!bundleList.size())
  103. {
  104. AZ_Warning("BundlingSystem", false, "Failed to locate bundles of type %s in folder %s", bundleExtension, bundleFolder);
  105. return;
  106. }
  107. auto archive = AZ::Interface<AZ::IO::IArchive>::Get();
  108. if (!archive)
  109. {
  110. AZ_Error("BundlingSystem", false, "Couldn't Get IArchive to load bundles!");
  111. return;
  112. }
  113. AZStd::lock_guard<AZStd::mutex> openBundleLock(m_bundleModeMutex);
  114. for (const auto& thisBundle : bundleList)
  115. {
  116. for (const auto& openedBundle : m_bundleModeBundles)
  117. {
  118. if (openedBundle == thisBundle)
  119. {
  120. continue;
  121. }
  122. }
  123. AZStd::string bundlePath;
  124. AZ::StringFunc::Path::Join(bundleRoot, thisBundle.c_str(), bundlePath);
  125. if (archive->OpenPack(bundleRoot, thisBundle.c_str()))
  126. {
  127. AZ_TracePrintf("BundlingSystem", "Loaded bundle %s\n",bundlePath.c_str());
  128. m_bundleModeBundles.emplace_back(AZStd::move(bundlePath));
  129. }
  130. else
  131. {
  132. AZ_TracePrintf("BundlingSystem", "Failed to load %s\n",bundlePath.c_str());
  133. }
  134. }
  135. }
  136. void BundlingSystemComponent::BundleOpened(const char* bundleName, AZStd::shared_ptr<AzFramework::AssetBundleManifest> bundleManifest, const char* nextBundle, AZStd::shared_ptr<AzFramework::AssetRegistry> bundleCatalog)
  137. {
  138. AZ_TracePrintf("BundlingSystem", "Opening bundle %s\n",bundleName);
  139. AZStd::lock_guard<AZStd::mutex> openBundleLock(m_openedBundleMutex);
  140. auto bundleIter = m_openedBundles.find(bundleName);
  141. if (bundleIter != m_openedBundles.end())
  142. {
  143. AZ_Warning("BundlingSystem", false, "Received BundleOpened message for bundle in records - %s", bundleName);
  144. return;
  145. }
  146. // Not already opened, new entry
  147. m_openedBundles[bundleName] = AZStd::make_unique<OpenBundleInfo>();
  148. AZStd::shared_ptr<AzFramework::AssetRegistry> nextCatalog; // Not all bundles will have catalogs - some are legacy.
  149. if (nextBundle == nullptr)
  150. {
  151. // Added to the end
  152. m_openedBundleList.push_back(bundleName);
  153. }
  154. else
  155. {
  156. for (auto openedListIter = m_openedBundleList.rbegin(); openedListIter != m_openedBundleList.rend(); ++openedListIter)
  157. {
  158. if (*openedListIter == nextBundle)
  159. {
  160. m_openedBundleList.insert(openedListIter.base(), bundleName);
  161. break;
  162. }
  163. auto openedBundleIter = m_openedBundles.find(*openedListIter);
  164. if (openedBundleIter == m_openedBundles.end())
  165. {
  166. AZ_Error("BundlingSystem", false, "Invalid bundle %s in openedList is not found in bundle map", openedListIter->c_str());
  167. }
  168. else if(openedBundleIter->second->m_catalog)
  169. {
  170. nextCatalog = openedBundleIter->second->m_catalog;
  171. }
  172. }
  173. }
  174. if (bundleManifest)
  175. {
  176. m_openedBundles[bundleName]->m_manifest = bundleManifest;
  177. m_openedBundles[bundleName]->m_catalog = AZStd::move(bundleCatalog);
  178. if (!m_openedBundles[bundleName]->m_catalog)
  179. {
  180. AZ_Error("BundlingSystem", false, "Failed to load catalog %s from bundle %s", bundleManifest->GetCatalogName().c_str(), bundleName);
  181. }
  182. bool catalogAdded{ false };
  183. AZ::Data::AssetCatalogRequestBus::BroadcastResult(catalogAdded, &AZ::Data::AssetCatalogRequestBus::Events::InsertDeltaCatalogBefore, m_openedBundles[bundleName]->m_catalog, nextCatalog);
  184. if (bundleManifest->GetDependentBundleNames().size())
  185. {
  186. OpenDependentBundles(bundleName, bundleManifest);
  187. }
  188. }
  189. else
  190. {
  191. AZ_TracePrintf("BundlingSystem", "No Manifest found - %s is a legacy Pak\n",bundleName);
  192. }
  193. }
  194. void BundlingSystemComponent::OpenDependentBundles(const char* bundleName, AZStd::shared_ptr<AzFramework::AssetBundleManifest> bundleManifest)
  195. {
  196. auto archive = AZ::Interface<AZ::IO::IArchive>::Get();
  197. if (!archive)
  198. {
  199. AZ_Error("BundlingSystem", false, "Couldn't Get IArchive to load dependent bundles for %s", bundleName);
  200. return;
  201. }
  202. AZStd::string folderPath;
  203. AZ::StringFunc::Path::GetFolderPath(bundleName, folderPath);
  204. for (const auto& thisBundle : bundleManifest->GetDependentBundleNames())
  205. {
  206. AZStd::string bundlePath;
  207. AZ::StringFunc::Path::Join(folderPath.c_str(), thisBundle.c_str(), bundlePath);
  208. if (!archive->OpenPack(bundleRoot, bundlePath.c_str()))
  209. {
  210. // We're not bailing here intentionally - try to open the remaining bundles
  211. AZ_Warning("BundlingSystem", false, "Failed to open dependent bundle %s of bundle %s", bundlePath.c_str(), bundleName);
  212. }
  213. }
  214. }
  215. void BundlingSystemComponent::BundleClosed(const char* bundleName)
  216. {
  217. AZ_TracePrintf("BundlingSystem", "Closing bundle %s\n",bundleName);
  218. AZStd::unique_ptr<OpenBundleInfo> bundleRecord;
  219. {
  220. AZStd::lock_guard<AZStd::mutex> openBundleLock(m_openedBundleMutex);
  221. auto bundleIter = m_openedBundles.find(bundleName);
  222. if (bundleIter == m_openedBundles.end())
  223. {
  224. AZ_Warning("BundlingSystem", false, "Failed to locate record for bundle %s", bundleName);
  225. return;
  226. }
  227. bundleRecord = AZStd::move(bundleIter->second);
  228. m_openedBundles.erase(bundleIter);
  229. for (auto openedListIter = m_openedBundleList.begin(); openedListIter != m_openedBundleList.end(); ++openedListIter)
  230. {
  231. if (*openedListIter == bundleName)
  232. {
  233. m_openedBundleList.erase(openedListIter);
  234. break;
  235. }
  236. }
  237. }
  238. bool catalogRemoved{ false };
  239. if (bundleRecord && bundleRecord->m_catalog)
  240. {
  241. AZ::Data::AssetCatalogRequestBus::BroadcastResult(catalogRemoved, &AZ::Data::AssetCatalogRequestBus::Events::RemoveDeltaCatalog, bundleRecord->m_catalog);
  242. if (bundleRecord->m_manifest->GetDependentBundleNames().size())
  243. {
  244. CloseDependentBundles(bundleName, bundleRecord->m_manifest);
  245. }
  246. }
  247. }
  248. void BundlingSystemComponent::CloseDependentBundles(const char* bundleName, AZStd::shared_ptr<AzFramework::AssetBundleManifest> bundleManifest)
  249. {
  250. auto archive = AZ::Interface<AZ::IO::IArchive>::Get();
  251. if (!archive)
  252. {
  253. AZ_Error("BundlingSystem", false, "Couldn't get IArchive to close dependent bundles for %s", bundleName);
  254. return;
  255. }
  256. AZStd::string folderPath;
  257. AZ::StringFunc::Path::GetFolderPath(bundleName, folderPath);
  258. for (const auto& thisBundle : bundleManifest->GetDependentBundleNames())
  259. {
  260. AZStd::string bundlePath;
  261. AZ::StringFunc::Path::Join(folderPath.c_str(), thisBundle.c_str(), bundlePath);
  262. if (!archive->ClosePack(bundlePath.c_str()))
  263. {
  264. // We're not bailing here intentionally - try to close the remaining bundles
  265. AZ_Warning("BundlingSystem", false, "Failed to close dependent bundle %s of bundle %s", bundlePath.c_str(), bundleName);
  266. }
  267. }
  268. }
  269. size_t BundlingSystemComponent::GetOpenedBundleCount() const
  270. {
  271. AZStd::lock_guard<AZStd::mutex> openBundleLock(m_openedBundleMutex);
  272. size_t bundleCount = m_openedBundleList.size();
  273. AZ_Assert(bundleCount == m_openedBundles.size(), "Bundle count does not match - %d vs %d", bundleCount, m_openedBundles.size());
  274. return bundleCount;
  275. }
  276. }