SpawnableLevelSystem.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  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 "CrySystem_precompiled.h"
  9. #include "SpawnableLevelSystem.h"
  10. #include "IMovieSystem.h"
  11. #include <CryCommon/LoadScreenBus.h>
  12. #include <AzFramework/API/ApplicationAPI.h>
  13. #include <AzFramework/IO/FileOperations.h>
  14. #include <AzFramework/Entity/GameEntityContextBus.h>
  15. #include <AzFramework/Input/Buses/Requests/InputChannelRequestBus.h>
  16. #include "MainThreadRenderRequestBus.h"
  17. #include <AzCore/Component/TickBus.h>
  18. #include <AzCore/IO/Path/Path.h>
  19. #include <AzCore/Settings/SettingsRegistryVisitorUtils.h>
  20. #include <AzCore/StringFunc/StringFunc.h>
  21. #include <AzCore/Script/ScriptSystemBus.h>
  22. #include <AzCore/Time/ITime.h>
  23. namespace LegacyLevelSystem
  24. {
  25. constexpr AZStd::string_view DeferredLoadLevelKey = "/O3DE/Runtime/SpawnableLevelSystem/DeferredLoadLevel";
  26. //------------------------------------------------------------------------
  27. static void LoadLevel(const AZ::ConsoleCommandContainer& arguments)
  28. {
  29. AZ_Error("SpawnableLevelSystem", !arguments.empty(), "LoadLevel requires a level file name to be provided.");
  30. AZ_Error("SpawnableLevelSystem", arguments.size() == 1, "LoadLevel requires a single level file name to be provided.");
  31. if (!arguments.empty() && gEnv && gEnv->pSystem && gEnv->pSystem->GetILevelSystem() && !gEnv->IsEditor())
  32. {
  33. gEnv->pSystem->GetILevelSystem()->LoadLevel(arguments[0].data());
  34. }
  35. else if (!arguments.empty())
  36. {
  37. // The SpawnableLevelSystem isn't available yet.
  38. // Defer the level load until later by storing it in the SettingsRegistry
  39. if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
  40. {
  41. settingsRegistry->Set(DeferredLoadLevelKey, arguments.front());
  42. }
  43. }
  44. }
  45. //------------------------------------------------------------------------
  46. static void UnloadLevel([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
  47. {
  48. AZ_Warning("SpawnableLevelSystem", !arguments.empty(), "UnloadLevel doesn't use any arguments.");
  49. if (gEnv && gEnv->pSystem && gEnv->pSystem->GetILevelSystem() && !gEnv->IsEditor())
  50. {
  51. gEnv->pSystem->GetILevelSystem()->UnloadLevel();
  52. }
  53. }
  54. AZ_CONSOLEFREEFUNC(LoadLevel, AZ::ConsoleFunctorFlags::Null, "Unloads the current level and loads a new one with the given asset name");
  55. AZ_CONSOLEFREEFUNC(UnloadLevel, AZ::ConsoleFunctorFlags::Null, "Unloads the current level");
  56. //------------------------------------------------------------------------
  57. SpawnableLevelSystem::SpawnableLevelSystem([[maybe_unused]] ISystem* pSystem)
  58. {
  59. CRY_ASSERT(pSystem);
  60. m_fLastLevelLoadTime = 0;
  61. m_fLastTime = 0;
  62. m_bLevelLoaded = false;
  63. m_levelLoadStartTime.SetValue(0);
  64. m_nLoadedLevelsCount = 0;
  65. AZ_Assert(gEnv && gEnv->pCryPak, "gEnv and CryPak must be initialized for loading levels.");
  66. if (!gEnv || !gEnv->pCryPak)
  67. {
  68. return;
  69. }
  70. AzFramework::RootSpawnableNotificationBus::Handler::BusConnect();
  71. AZ_Error("SpawnableLevelSystem", AzFramework::LevelSystemLifecycleInterface::Get() == this,
  72. "Failed to register the SpawnableLevelSystem with the LevelSystemLifecycleInterface.");
  73. // If there were LoadLevel command invocations before the creation of the level system
  74. // then those invocations were queued.
  75. // load the last level in the queue, since only one level can be loaded at a time
  76. if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
  77. {
  78. if (AZ::SettingsRegistryInterface::FixedValueString deferredLevelName;
  79. settingsRegistry->Get(deferredLevelName, DeferredLoadLevelKey) && !deferredLevelName.empty())
  80. {
  81. // since this is the constructor any derived classes vtables aren't setup yet
  82. // call this class LoadLevel function
  83. AZ_TracePrintf("SpawnableLevelSystem", "The Level System is now available."
  84. " Loading level %s which could not be loaded earlier\n", deferredLevelName.c_str());
  85. SpawnableLevelSystem::LoadLevel(deferredLevelName.c_str());
  86. // Delete the key with the deferred level name
  87. settingsRegistry->Remove(DeferredLoadLevelKey);
  88. }
  89. }
  90. }
  91. //------------------------------------------------------------------------
  92. SpawnableLevelSystem::~SpawnableLevelSystem()
  93. {
  94. AzFramework::RootSpawnableNotificationBus::Handler::BusDisconnect();
  95. }
  96. void SpawnableLevelSystem::Release()
  97. {
  98. delete this;
  99. }
  100. bool SpawnableLevelSystem::IsLevelLoaded() const
  101. {
  102. return m_bLevelLoaded;
  103. }
  104. const char* SpawnableLevelSystem::GetCurrentLevelName() const
  105. {
  106. return m_bLevelLoaded ? m_lastLevelName.c_str() : "";
  107. }
  108. // If the level load failed then we need to have a different shutdown procedure vs when a level is naturally unloaded
  109. void SpawnableLevelSystem::SetLevelLoadFailed(bool loadFailed)
  110. {
  111. m_levelLoadFailed = loadFailed;
  112. }
  113. bool SpawnableLevelSystem::GetLevelLoadFailed()
  114. {
  115. return m_levelLoadFailed;
  116. }
  117. AZ::Data::AssetType SpawnableLevelSystem::GetLevelAssetType() const
  118. {
  119. return azrtti_typeid<AzFramework::Spawnable>();
  120. }
  121. // The following methods are deprecated from ILevelSystem and will be removed once slice support is removed.
  122. // [LYN-2376] Remove once legacy slice support is removed
  123. void SpawnableLevelSystem::Rescan([[maybe_unused]] const char* levelsFolder)
  124. {
  125. AZ_Assert(false, "Rescan - No longer supported.");
  126. }
  127. // [LYN-2376] Remove once legacy slice support is removed
  128. int SpawnableLevelSystem::GetLevelCount()
  129. {
  130. AZ_Assert(false, "GetLevelCount - No longer supported.");
  131. return 0;
  132. }
  133. // [LYN-2376] Remove once legacy slice support is removed
  134. ILevelInfo* SpawnableLevelSystem::GetLevelInfo([[maybe_unused]] int level)
  135. {
  136. AZ_Assert(false, "GetLevelInfo - No longer supported.");
  137. return nullptr;
  138. }
  139. // [LYN-2376] Remove once legacy slice support is removed
  140. ILevelInfo* SpawnableLevelSystem::GetLevelInfo([[maybe_unused]] const char* levelName)
  141. {
  142. AZ_Assert(false, "GetLevelInfo - No longer supported.");
  143. return nullptr;
  144. }
  145. //------------------------------------------------------------------------
  146. void SpawnableLevelSystem::AddListener(ILevelSystemListener* pListener)
  147. {
  148. AZStd::vector<ILevelSystemListener*>::iterator it = AZStd::find(m_listeners.begin(), m_listeners.end(), pListener);
  149. if (it == m_listeners.end())
  150. {
  151. m_listeners.push_back(pListener);
  152. }
  153. }
  154. //------------------------------------------------------------------------
  155. void SpawnableLevelSystem::RemoveListener(ILevelSystemListener* pListener)
  156. {
  157. AZStd::vector<ILevelSystemListener*>::iterator it = AZStd::find(m_listeners.begin(), m_listeners.end(), pListener);
  158. if (it != m_listeners.end())
  159. {
  160. m_listeners.erase(it);
  161. }
  162. }
  163. //------------------------------------------------------------------------
  164. bool SpawnableLevelSystem::LoadLevel(const char* levelName)
  165. {
  166. if (gEnv->IsEditor())
  167. {
  168. AZ_TracePrintf("CrySystem::SpawnableLevelSystem", "LoadLevel for %s was called in the editor - not actually loading.\n", levelName);
  169. return false;
  170. }
  171. // Make sure a spawnable level exists that matches levelname
  172. AZStd::string validLevelName;
  173. AZ::Data::AssetId rootSpawnableAssetId;
  174. AZ::Data::AssetCatalogRequestBus::BroadcastResult(
  175. rootSpawnableAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, levelName, AZ::Data::AssetType{}, false);
  176. if (rootSpawnableAssetId.IsValid())
  177. {
  178. validLevelName = levelName;
  179. }
  180. else
  181. {
  182. // It's common for users to only provide the level name, but not the full asset path
  183. // Example: "MyLevel" instead of "Levels/MyLevel/MyLevel.spawnable"
  184. if (!AZ::IO::PathView(levelName).HasExtension())
  185. {
  186. // Search inside the "Levels" folder for a level spawnable matching levelname
  187. const AZStd::string possibleLevelAssetPath = AZStd::string::format("Levels/%s/%s.spawnable", levelName, levelName);
  188. AZ::Data::AssetCatalogRequestBus::BroadcastResult(
  189. rootSpawnableAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, possibleLevelAssetPath.c_str(),
  190. AZ::Data::AssetType{}, false);
  191. if (rootSpawnableAssetId.IsValid())
  192. {
  193. validLevelName = possibleLevelAssetPath;
  194. }
  195. }
  196. }
  197. if (validLevelName.empty())
  198. {
  199. OnLevelNotFound(levelName);
  200. return false;
  201. }
  202. // This is a valid level, find out if any systems need to stop level loading before proceeding
  203. bool blockLoading = false;
  204. AzFramework::LevelLoadBlockerBus::EnumerateHandlers(
  205. [&blockLoading, &validLevelName](AzFramework::LevelLoadBlockerRequests* handler) -> bool
  206. {
  207. if (handler->ShouldBlockLevelLoading(validLevelName.c_str()))
  208. {
  209. blockLoading = true;
  210. return false; // Stop iterating handlers. This level should be blocked.
  211. }
  212. return true;
  213. });
  214. if (blockLoading)
  215. {
  216. AZ_TracePrintf("CrySystem::SpawnableLevelSystem", "LoadLevel for %s was blocked.\n", validLevelName.c_str());
  217. return false;
  218. }
  219. // If a level is currently loaded, unload it before loading the next one.
  220. if (IsLevelLoaded())
  221. {
  222. UnloadLevel();
  223. }
  224. gEnv->pSystem->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_LOAD_PREPARE, 0, 0);
  225. PrepareNextLevel(validLevelName.c_str());
  226. bool result = LoadLevelInternal(validLevelName.c_str());
  227. if (result)
  228. {
  229. OnLoadingComplete(validLevelName.c_str());
  230. }
  231. return result;
  232. }
  233. //------------------------------------------------------------------------
  234. bool SpawnableLevelSystem::LoadLevelInternal(const char* levelName)
  235. {
  236. gEnv->pSystem->SetSystemGlobalState(ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_START);
  237. INDENT_LOG_DURING_SCOPE();
  238. AZ::Data::AssetId rootSpawnableAssetId;
  239. AZ::Data::AssetCatalogRequestBus::BroadcastResult(
  240. rootSpawnableAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, levelName, AZ::Data::AssetType{}, false);
  241. if (!rootSpawnableAssetId.IsValid())
  242. {
  243. OnLoadingError(levelName, "AssetCatalog has no entry for the requested level.");
  244. return false;
  245. }
  246. // This scope is specifically used for marking a loading time profile section
  247. {
  248. m_bLevelLoaded = false;
  249. m_lastLevelName = levelName;
  250. gEnv->pConsole->SetScrollMax(600);
  251. ICVar* con_showonload = gEnv->pConsole->GetCVar("con_showonload");
  252. if (con_showonload && con_showonload->GetIVal() != 0)
  253. {
  254. gEnv->pConsole->ShowConsole(true);
  255. ICVar* g_enableloadingscreen = gEnv->pConsole->GetCVar("g_enableloadingscreen");
  256. if (g_enableloadingscreen)
  257. {
  258. g_enableloadingscreen->Set(0);
  259. }
  260. }
  261. // This is a workaround until the replacement for GameEntityContext is done
  262. AzFramework::GameEntityContextEventBus::Broadcast(&AzFramework::GameEntityContextEventBus::Events::OnPreGameEntitiesStarted);
  263. OnLoadingStart(levelName);
  264. auto pPak = gEnv->pCryPak;
  265. ICVar* pSpamDelay = gEnv->pConsole->GetCVar("log_SpamDelay");
  266. float spamDelay = 0.0f;
  267. if (pSpamDelay)
  268. {
  269. spamDelay = pSpamDelay->GetFVal();
  270. pSpamDelay->Set(0.0f);
  271. }
  272. AZ::Data::Asset<AzFramework::Spawnable> rootSpawnable(
  273. rootSpawnableAssetId, azrtti_typeid<AzFramework::Spawnable>(), levelName);
  274. m_rootSpawnableId = rootSpawnableAssetId;
  275. m_rootSpawnableGeneration = AzFramework::RootSpawnableInterface::Get()->AssignRootSpawnable(rootSpawnable);
  276. // This is a workaround until the replacement for GameEntityContext is done
  277. AzFramework::GameEntityContextEventBus::Broadcast(&AzFramework::GameEntityContextEventBus::Events::OnGameEntitiesStarted);
  278. //////////////////////////////////////////////////////////////////////////
  279. // Movie system must be reset after entities.
  280. //////////////////////////////////////////////////////////////////////////
  281. IMovieSystem* movieSystem = AZ::Interface<IMovieSystem>::Get();
  282. if (movieSystem)
  283. {
  284. // bSeekAllToStart needs to be false here as it's only of interest in the editor
  285. constexpr bool playOnReset = true;
  286. constexpr bool seekToStart = false;
  287. movieSystem->Reset(playOnReset, seekToStart);
  288. }
  289. gEnv->pSystem->SetSystemGlobalState(ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_START_PRECACHE);
  290. //////////////////////////////////////////////////////////////////////////
  291. //////////////////////////////////////////////////////////////////////////
  292. gEnv->pConsole->SetScrollMax(600 / 2);
  293. pPak->GetResourceList(AZ::IO::IArchive::RFOM_NextLevel)->Clear();
  294. if (pSpamDelay)
  295. {
  296. pSpamDelay->Set(spamDelay);
  297. }
  298. m_bLevelLoaded = true;
  299. gEnv->pSystem->SetSystemGlobalState(ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_END);
  300. }
  301. GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_LOAD_END, 0, 0);
  302. if (auto cvar = gEnv->pConsole->GetCVar("sv_map"); cvar)
  303. {
  304. cvar->Set(levelName);
  305. }
  306. gEnv->pSystem->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_PRECACHE_START, 0, 0);
  307. return true;
  308. }
  309. //------------------------------------------------------------------------
  310. void SpawnableLevelSystem::PrepareNextLevel(const char* levelName)
  311. {
  312. AZ::Data::AssetId rootSpawnableAssetId;
  313. AZ::Data::AssetCatalogRequestBus::BroadcastResult(
  314. rootSpawnableAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, levelName, AZ::Data::AssetType{}, false);
  315. if (!rootSpawnableAssetId.IsValid())
  316. {
  317. // alert the listener
  318. OnLevelNotFound(levelName);
  319. return;
  320. }
  321. // This work not required in-editor.
  322. if (!gEnv || !gEnv->IsEditor())
  323. {
  324. const AZ::TimeMs timeMs = AZ::GetRealElapsedTimeMs();
  325. const double timeSec = AZ::TimeMsToSecondsDouble(timeMs);
  326. m_levelLoadStartTime = CTimeValue(timeSec);
  327. // switched to level heap, so now imm start the loading screen (renderer will be reinitialized in the levelheap)
  328. gEnv->pSystem->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_LOAD_START_LOADINGSCREEN, 0, 0);
  329. gEnv->pSystem->SetSystemGlobalState(ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_START_PREPARE);
  330. }
  331. OnPrepareNextLevel(levelName);
  332. }
  333. void SpawnableLevelSystem::OnPrepareNextLevel(const char* levelName)
  334. {
  335. AZ_TracePrintf("LevelSystem", "Level system is preparing to load '%s'\n", levelName);
  336. for (auto& listener : m_listeners)
  337. {
  338. listener->OnPrepareNextLevel(levelName);
  339. }
  340. AzFramework::LevelSystemLifecycleNotificationBus::Broadcast(
  341. &AzFramework::LevelSystemLifecycleNotifications::OnPrepareNextLevel, levelName);
  342. }
  343. //------------------------------------------------------------------------
  344. void SpawnableLevelSystem::OnLevelNotFound(const char* levelName)
  345. {
  346. AZ_Error("LevelSystem", false, "Requested level not found: '%s'\n", levelName);
  347. for (auto& listener : m_listeners)
  348. {
  349. listener->OnLevelNotFound(levelName);
  350. }
  351. AzFramework::LevelSystemLifecycleNotificationBus::Broadcast(
  352. &AzFramework::LevelSystemLifecycleNotifications::OnLevelNotFound, levelName);
  353. }
  354. //------------------------------------------------------------------------
  355. void SpawnableLevelSystem::OnLoadingStart(const char* levelName)
  356. {
  357. AZ_TracePrintf("LevelSystem", "Level system is loading '%s'\n", levelName);
  358. if (gEnv->pCryPak->GetRecordFileOpenList() == AZ::IO::IArchive::RFOM_EngineStartup)
  359. {
  360. gEnv->pCryPak->RecordFileOpen(AZ::IO::IArchive::RFOM_Level);
  361. }
  362. const AZ::TimeMs timeMs = AZ::GetRealElapsedTimeMs();
  363. m_fLastTime = AZ::TimeMsToSeconds(timeMs);
  364. GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_LOAD_START, 0, 0);
  365. for (auto& listener : m_listeners)
  366. {
  367. listener->OnLoadingStart(levelName);
  368. }
  369. AzFramework::LevelSystemLifecycleNotificationBus::Broadcast(
  370. &AzFramework::LevelSystemLifecycleNotifications::OnLoadingStart, levelName);
  371. }
  372. //------------------------------------------------------------------------
  373. void SpawnableLevelSystem::OnLoadingError(const char* levelName, const char* error)
  374. {
  375. AZ_Error("LevelSystem", false, "Error loading level '%s': %s\n", levelName, error);
  376. for (auto& listener : m_listeners)
  377. {
  378. listener->OnLoadingError(levelName, error);
  379. }
  380. AzFramework::LevelSystemLifecycleNotificationBus::Broadcast(
  381. &AzFramework::LevelSystemLifecycleNotifications::OnLoadingError, levelName, error);
  382. }
  383. //------------------------------------------------------------------------
  384. void SpawnableLevelSystem::OnLoadingComplete(const char* levelName)
  385. {
  386. const AZ::TimeMs timeMs = AZ::GetRealElapsedTimeMs();
  387. const double timeSec = AZ::TimeMsToSecondsDouble(timeMs);
  388. const CTimeValue t(timeSec);
  389. m_fLastLevelLoadTime = (t - m_levelLoadStartTime).GetSeconds();
  390. LogLoadingTime();
  391. m_nLoadedLevelsCount++;
  392. // Hide console after loading.
  393. gEnv->pConsole->ShowConsole(false);
  394. for (auto& listener : m_listeners)
  395. {
  396. listener->OnLoadingComplete(levelName);
  397. }
  398. AzFramework::LevelSystemLifecycleNotificationBus::Broadcast(
  399. &AzFramework::LevelSystemLifecycleNotifications::OnLoadingComplete, levelName);
  400. #if AZ_LOADSCREENCOMPONENT_ENABLED
  401. EBUS_EVENT(LoadScreenBus, Stop);
  402. #endif // if AZ_LOADSCREENCOMPONENT_ENABLED
  403. AZ_TracePrintf("LevelSystem", "Level load complete: '%s'\n", levelName);
  404. }
  405. //------------------------------------------------------------------------
  406. void SpawnableLevelSystem::OnLoadingProgress(const char* levelName, int progressAmount)
  407. {
  408. for (auto& listener : m_listeners)
  409. {
  410. listener->OnLoadingProgress(levelName, progressAmount);
  411. }
  412. AzFramework::LevelSystemLifecycleNotificationBus::Broadcast(
  413. &AzFramework::LevelSystemLifecycleNotifications::OnLoadingProgress, levelName, progressAmount);
  414. }
  415. //------------------------------------------------------------------------
  416. void SpawnableLevelSystem::OnUnloadComplete(const char* levelName)
  417. {
  418. for (auto& listener : m_listeners)
  419. {
  420. listener->OnUnloadComplete(levelName);
  421. }
  422. AzFramework::LevelSystemLifecycleNotificationBus::Broadcast(
  423. &AzFramework::LevelSystemLifecycleNotifications::OnUnloadComplete, levelName);
  424. AZ_TracePrintf("LevelSystem", "Level unload complete: '%s'\n", levelName);
  425. }
  426. //////////////////////////////////////////////////////////////////////////
  427. void SpawnableLevelSystem::LogLoadingTime()
  428. {
  429. if (gEnv->IsEditor())
  430. {
  431. return;
  432. }
  433. if (!GetISystem()->IsDevMode())
  434. {
  435. return;
  436. }
  437. char vers[128];
  438. GetISystem()->GetFileVersion().ToString(vers, sizeof(vers));
  439. const char* sChain = "";
  440. if (m_nLoadedLevelsCount > 0)
  441. {
  442. sChain = " (Chained)";
  443. }
  444. gEnv->pLog->Log("Game Level Load Time: [%s] Level %s loaded in %.2f seconds%s", vers, m_lastLevelName.c_str(), m_fLastLevelLoadTime, sChain);
  445. }
  446. //////////////////////////////////////////////////////////////////////////
  447. void SpawnableLevelSystem::UnloadLevel()
  448. {
  449. if (gEnv->IsEditor())
  450. {
  451. return;
  452. }
  453. if (m_lastLevelName.empty())
  454. {
  455. return;
  456. }
  457. AZ_TracePrintf("LevelSystem", "UnloadLevel Start\n");
  458. INDENT_LOG_DURING_SCOPE();
  459. // Flush core buses. We're about to unload Cry modules and need to ensure we don't have module-owned functions left behind.
  460. AZ::Data::AssetBus::ExecuteQueuedEvents();
  461. AZ::TickBus::ExecuteQueuedEvents();
  462. AZ::MainThreadRenderRequestBus::ExecuteQueuedEvents();
  463. if (gEnv && gEnv->pSystem)
  464. {
  465. // clear all error messages to prevent stalling due to runtime file access check during chainloading
  466. gEnv->pSystem->ClearErrorMessages();
  467. }
  468. if (gEnv && gEnv->pCryPak)
  469. {
  470. gEnv->pCryPak->DisableRuntimeFileAccess(false);
  471. }
  472. const AZ::TimeMs beginTimeMs = AZ::GetRealElapsedTimeMs();
  473. IMovieSystem* movieSystem = AZ::Interface<IMovieSystem>::Get();
  474. if (movieSystem)
  475. {
  476. constexpr bool playOnReset = false;
  477. constexpr bool seekToStart = false;
  478. movieSystem->Reset(playOnReset, seekToStart);
  479. movieSystem->RemoveAllSequences();
  480. }
  481. OnUnloadComplete(m_lastLevelName.c_str());
  482. // Delete level entities and remove them from the game entity context
  483. AzFramework::RootSpawnableInterface::Get()->ReleaseRootSpawnable();
  484. // Process the queued deactivate calls for the unloaded entities
  485. AzFramework::RootSpawnableInterface::Get()->ProcessSpawnableQueueUntilEmpty();
  486. m_lastLevelName.clear();
  487. // Force Lua garbage collection (may no longer be needed now the legacy renderer has been removed).
  488. // Normally the GC step is triggered at the end of this method (by the ESYSTEM_EVENT_LEVEL_POST_UNLOAD event).
  489. EBUS_EVENT(AZ::ScriptSystemRequestBus, GarbageCollect);
  490. m_bLevelLoaded = false;
  491. [[maybe_unused]] const AZ::TimeMs unloadTimeMs = AZ::GetRealElapsedTimeMs() - beginTimeMs;
  492. AZ_TracePrintf("LevelSystem", "UnloadLevel End: %.1f sec\n", AZ::TimeMsToSeconds(unloadTimeMs));
  493. // Must be sent last.
  494. // Cleanup all containers
  495. GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_POST_UNLOAD, 0, 0);
  496. AzFramework::InputChannelRequestBus::Broadcast(&AzFramework::InputChannelRequests::ResetState);
  497. AzFramework::GameEntityContextEventBus::Broadcast(&AzFramework::GameEntityContextEventBus::Events::OnGameEntitiesReset);
  498. }
  499. void SpawnableLevelSystem::OnRootSpawnableAssigned(
  500. [[maybe_unused]] AZ::Data::Asset<AzFramework::Spawnable> rootSpawnable, [[maybe_unused]] uint32_t generation)
  501. {
  502. }
  503. void SpawnableLevelSystem::OnRootSpawnableReleased([[maybe_unused]] uint32_t generation)
  504. {
  505. }
  506. } // namespace LegacyLevelSystem