LevelSystem.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  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. // [LYN-2376] Remove the entire file once legacy slice support is removed
  9. #include "CrySystem_precompiled.h"
  10. #include "LevelSystem.h"
  11. #include "IMovieSystem.h"
  12. #include <ILocalizationManager.h>
  13. #include "CryPath.h"
  14. #include <CryCommon/LoadScreenBus.h>
  15. #include <AzCore/Time/ITime.h>
  16. #include <AzFramework/IO/FileOperations.h>
  17. #include <AzFramework/Entity/GameEntityContextBus.h>
  18. #include <AzFramework/Input/Buses/Requests/InputChannelRequestBus.h>
  19. #include <AzFramework/Spawnable/RootSpawnableInterface.h>
  20. #include "MainThreadRenderRequestBus.h"
  21. #include <AzCore/Component/TickBus.h>
  22. #include <AzCore/IO/Path/Path.h>
  23. #include <AzCore/StringFunc/StringFunc.h>
  24. #include <AzCore/Script/ScriptSystemBus.h>
  25. namespace LegacyLevelSystem
  26. {
  27. static constexpr const char* ArchiveExtension = ".pak";
  28. //////////////////////////////////////////////////////////////////////////
  29. bool CLevelInfo::OpenLevelPak()
  30. {
  31. // The prefab system doesn't use level.pak
  32. return false;
  33. }
  34. //////////////////////////////////////////////////////////////////////////
  35. void CLevelInfo::CloseLevelPak()
  36. {
  37. // The prefab system doesn't use level.pak
  38. return;
  39. }
  40. //////////////////////////////////////////////////////////////////////////
  41. bool CLevelInfo::ReadInfo()
  42. {
  43. // Set up a default game type for legacy code.
  44. m_defaultGameTypeName = "mission0";
  45. // Prefabs do not use Pak files.
  46. return true;
  47. }
  48. //////////////////////////////////////////////////////////////////////////
  49. /// Used by console auto completion.
  50. struct SLevelNameAutoComplete
  51. : public IConsoleArgumentAutoComplete
  52. {
  53. AZStd::vector<AZStd::string> levels;
  54. virtual int GetCount() const { return static_cast<int>(levels.size()); };
  55. virtual const char* GetValue(int nIndex) const { return levels[nIndex].c_str(); };
  56. };
  57. static SLevelNameAutoComplete g_LevelNameAutoComplete;
  58. //------------------------------------------------------------------------
  59. CLevelSystem::CLevelSystem(ISystem* pSystem, const char* levelsFolder)
  60. : m_pSystem(pSystem)
  61. , m_pCurrentLevel(0)
  62. , m_pLoadingLevelInfo(0)
  63. {
  64. CRY_ASSERT(pSystem);
  65. //if (!gEnv->IsEditor())
  66. Rescan(levelsFolder);
  67. m_fLastLevelLoadTime = 0;
  68. m_fLastTime = 0;
  69. m_bLevelLoaded = false;
  70. m_levelLoadStartTime.SetValue(0);
  71. m_nLoadedLevelsCount = 0;
  72. gEnv->pConsole->RegisterAutoComplete("LoadLevel", &g_LevelNameAutoComplete);
  73. AZ_Assert(gEnv && gEnv->pCryPak, "gEnv and CryPak must be initialized for loading levels.");
  74. if (!gEnv || !gEnv->pCryPak)
  75. {
  76. return;
  77. }
  78. auto archive = AZ::Interface<AZ::IO::IArchive>::Get();
  79. if (AZ::IO::IArchive::LevelPackOpenEvent* levelPakOpenEvent = archive->GetLevelPackOpenEvent())
  80. {
  81. m_levelPackOpenHandler = AZ::IO::IArchive::LevelPackOpenEvent::Handler([this](const AZStd::vector<AZ::IO::Path>& levelDirs)
  82. {
  83. for (AZ::IO::Path levelDir : levelDirs)
  84. {
  85. bool modFolder = false;
  86. PopulateLevels((levelDir / "*").Native(), levelDir.Native(), AZ::Interface<AZ::IO::IArchive>::Get(), modFolder, false);
  87. }
  88. });
  89. m_levelPackOpenHandler.Connect(*levelPakOpenEvent);
  90. }
  91. if (AZ::IO::IArchive::LevelPackCloseEvent* levelPakCloseEvent = archive->GetLevelPackCloseEvent())
  92. {
  93. m_levelPackCloseHandler = AZ::IO::IArchive::LevelPackCloseEvent::Handler([this](AZStd::string_view)
  94. {
  95. Rescan(ILevelSystem::LevelsDirectoryName);
  96. });
  97. m_levelPackCloseHandler.Connect(*levelPakCloseEvent);
  98. }
  99. AZ_Error("LevelSystem", AzFramework::LevelSystemLifecycleInterface::Get() == this,
  100. "Failed to register the LevelSystem with the LevelSystemLifecycleInterface.");
  101. }
  102. //------------------------------------------------------------------------
  103. CLevelSystem::~CLevelSystem()
  104. {
  105. UnloadLevel();
  106. }
  107. //------------------------------------------------------------------------
  108. void CLevelSystem::Rescan(const char* levelsFolder)
  109. {
  110. if (levelsFolder)
  111. {
  112. m_levelsFolder = levelsFolder;
  113. }
  114. CRY_ASSERT(!m_levelsFolder.empty());
  115. m_levelInfos.clear();
  116. m_levelInfos.reserve(64);
  117. ScanFolder(0, false);
  118. g_LevelNameAutoComplete.levels.clear();
  119. for (const CLevelInfo& levelInfo : m_levelInfos)
  120. {
  121. g_LevelNameAutoComplete.levels.emplace_back(AZ::IO::PathView(levelInfo.GetName()).Stem().Native());
  122. }
  123. }
  124. //-----------------------------------------------------------------------
  125. void CLevelSystem::ScanFolder(const char* subfolder, bool modFolder)
  126. {
  127. AZStd::string folder;
  128. if (subfolder && subfolder[0])
  129. {
  130. folder = subfolder;
  131. }
  132. AZStd::string search(m_levelsFolder);
  133. if (!folder.empty())
  134. {
  135. if (AZ::StringFunc::StartsWith(folder.c_str(), m_levelsFolder.c_str()))
  136. {
  137. search = folder;
  138. }
  139. else
  140. {
  141. search += "/" + folder;
  142. }
  143. }
  144. search += "/*";
  145. AZ_Assert(gEnv && gEnv->pCryPak, "gEnv and must be initialized for loading levels.");
  146. if (!gEnv || !gEnv->pCryPak)
  147. {
  148. return;
  149. }
  150. auto pPak = gEnv->pCryPak;
  151. AZStd::unordered_set<AZStd::string> pakList;
  152. AZ::IO::ArchiveFileIterator handle = pPak->FindFirst(search.c_str(), AZ::IO::FileSearchLocation::OnDisk);
  153. if (handle)
  154. {
  155. do
  156. {
  157. AZStd::string extension;
  158. AZStd::string levelName;
  159. AZ::StringFunc::Path::Split(handle.m_filename.data(), nullptr, nullptr, &levelName, &extension);
  160. if (extension == ArchiveExtension)
  161. {
  162. if (AZ::StringFunc::Equal(handle.m_filename.data(), LevelPakName))
  163. {
  164. // level folder contain pak files like 'level.pak'
  165. // which we only want to load during level loading.
  166. continue;
  167. }
  168. AZStd::string levelContainerPakPath;
  169. AZ::StringFunc::Path::Join("@products@", m_levelsFolder.c_str(), levelContainerPakPath);
  170. if (subfolder && subfolder[0])
  171. {
  172. AZ::StringFunc::Path::Join(levelContainerPakPath.c_str(), subfolder, levelContainerPakPath);
  173. }
  174. AZ::StringFunc::Path::Join(levelContainerPakPath.c_str(), handle.m_filename.data(), levelContainerPakPath);
  175. pakList.emplace(levelContainerPakPath);
  176. continue;
  177. }
  178. } while (handle = pPak->FindNext(handle));
  179. pPak->FindClose(handle);
  180. }
  181. // Open all the available paks found in the levels folder
  182. for (auto iter = pakList.begin(); iter != pakList.end(); iter++)
  183. {
  184. gEnv->pCryPak->OpenPack(iter->c_str(), nullptr, nullptr, false);
  185. }
  186. // Levels in bundles now take priority over levels outside of bundles.
  187. PopulateLevels(search, folder, pPak, modFolder, false);
  188. // Load levels outside of the bundles to maintain backward compatibility.
  189. PopulateLevels(search, folder, pPak, modFolder, true);
  190. }
  191. void CLevelSystem::PopulateLevels(
  192. AZStd::string searchPattern, const AZStd::string& folder, AZ::IO::IArchive* pPak, bool& modFolder, bool fromFileSystemOnly)
  193. {
  194. // allow this find first to actually touch the file system
  195. // (causes small overhead but with minimal amount of levels this should only be around 150ms on actual DVD Emu)
  196. AZ::IO::ArchiveFileIterator handle = pPak->FindFirst(searchPattern.c_str(),
  197. fromFileSystemOnly ? AZ::IO::FileSearchLocation::OnDisk : AZ::IO::FileSearchLocation::InPak);
  198. if (handle)
  199. {
  200. do
  201. {
  202. if ((handle.m_fileDesc.nAttrib & AZ::IO::FileDesc::Attribute::Subdirectory) != AZ::IO::FileDesc::Attribute::Subdirectory ||
  203. handle.m_filename == "." || handle.m_filename == "..")
  204. {
  205. continue;
  206. }
  207. AZStd::string levelFolder;
  208. if (fromFileSystemOnly)
  209. {
  210. levelFolder =
  211. (folder.empty() ? "" : (folder + "/")) + AZStd::string(handle.m_filename.data(), handle.m_filename.size());
  212. }
  213. else
  214. {
  215. AZStd::string levelName(AZ::IO::PathView(handle.m_filename).Filename().Native());
  216. levelFolder = (folder.empty() ? "" : (folder + "/")) + levelName;
  217. }
  218. AZStd::string levelPath;
  219. if (AZ::StringFunc::StartsWith(levelFolder.c_str(), m_levelsFolder.c_str()))
  220. {
  221. levelPath = levelFolder;
  222. }
  223. else
  224. {
  225. levelPath = m_levelsFolder + "/" + levelFolder;
  226. }
  227. const AZStd::string levelPakName = levelPath + "/" + LevelPakName;
  228. const AZStd::string levelInfoName = levelPath + "/levelinfo.xml";
  229. if (!pPak->IsFileExist(
  230. levelPakName.c_str(),
  231. fromFileSystemOnly ? AZ::IO::FileSearchLocation::OnDisk : AZ::IO::FileSearchLocation::InPak) &&
  232. !pPak->IsFileExist(
  233. levelInfoName.c_str(),
  234. fromFileSystemOnly ? AZ::IO::FileSearchLocation::OnDisk : AZ::IO::FileSearchLocation::InPak))
  235. {
  236. ScanFolder(levelFolder.c_str(), modFolder);
  237. continue;
  238. }
  239. // With the level.pak workflow, levelPath and levelName will point to a directory.
  240. // levelPath: levels/mylevel
  241. // levelName: mylevel
  242. CLevelInfo levelInfo;
  243. levelInfo.m_levelPath = levelPath;
  244. levelInfo.m_levelName = levelFolder;
  245. levelInfo.m_isPak = !fromFileSystemOnly;
  246. CLevelInfo* pExistingInfo = GetLevelInfoInternal(levelInfo.m_levelName);
  247. // Don't add the level if it is already in the list
  248. if (pExistingInfo == NULL)
  249. {
  250. m_levelInfos.push_back(levelInfo);
  251. }
  252. else
  253. {
  254. // Levels in bundles take priority over levels outside bundles.
  255. if (!pExistingInfo->m_isPak && levelInfo.m_isPak)
  256. {
  257. *pExistingInfo = levelInfo;
  258. }
  259. }
  260. } while (handle = pPak->FindNext(handle));
  261. pPak->FindClose(handle);
  262. }
  263. }
  264. //------------------------------------------------------------------------
  265. int CLevelSystem::GetLevelCount()
  266. {
  267. return (int)m_levelInfos.size();
  268. }
  269. //------------------------------------------------------------------------
  270. ILevelInfo* CLevelSystem::GetLevelInfo(int level)
  271. {
  272. return GetLevelInfoInternal(level);
  273. }
  274. //------------------------------------------------------------------------
  275. CLevelInfo* CLevelSystem::GetLevelInfoInternal(int level)
  276. {
  277. if ((level >= 0) && (level < GetLevelCount()))
  278. {
  279. return &m_levelInfos[level];
  280. }
  281. return 0;
  282. }
  283. //------------------------------------------------------------------------
  284. ILevelInfo* CLevelSystem::GetLevelInfo(const char* levelName)
  285. {
  286. return GetLevelInfoInternal(levelName);
  287. }
  288. //------------------------------------------------------------------------
  289. CLevelInfo* CLevelSystem::GetLevelInfoInternal(const AZStd::string& levelName)
  290. {
  291. // If level not found by full name try comparing with only filename
  292. for (AZStd::vector<CLevelInfo>::iterator it = m_levelInfos.begin(); it != m_levelInfos.end(); ++it)
  293. {
  294. if (!azstricmp(it->GetName(), levelName.c_str()))
  295. {
  296. return &(*it);
  297. }
  298. }
  299. //////////////////////////////////////////////////////////////////////////
  300. for (AZStd::vector<CLevelInfo>::iterator it = m_levelInfos.begin(); it != m_levelInfos.end(); ++it)
  301. {
  302. {
  303. if (!azstricmp(PathUtil::GetFileName(it->GetName()).c_str(), levelName.c_str()))
  304. {
  305. return &(*it);
  306. }
  307. }
  308. }
  309. // Try stripping out the folder to find the raw filename
  310. AZStd::string sLevelName(levelName);
  311. size_t lastSlash = sLevelName.find_last_of('\\');
  312. if (lastSlash == AZStd::string::npos)
  313. {
  314. lastSlash = sLevelName.find_last_of('/');
  315. }
  316. if (lastSlash != AZStd::string::npos)
  317. {
  318. sLevelName = sLevelName.substr(lastSlash + 1, sLevelName.size() - lastSlash - 1);
  319. return GetLevelInfoInternal(sLevelName.c_str());
  320. }
  321. return 0;
  322. }
  323. //------------------------------------------------------------------------
  324. void CLevelSystem::AddListener(ILevelSystemListener* pListener)
  325. {
  326. AZStd::vector<ILevelSystemListener*>::iterator it = AZStd::find(m_listeners.begin(), m_listeners.end(), pListener);
  327. if (it == m_listeners.end())
  328. {
  329. m_listeners.reserve(12);
  330. m_listeners.push_back(pListener);
  331. }
  332. }
  333. //------------------------------------------------------------------------
  334. void CLevelSystem::RemoveListener(ILevelSystemListener* pListener)
  335. {
  336. AZStd::vector<ILevelSystemListener*>::iterator it = AZStd::find(m_listeners.begin(), m_listeners.end(), pListener);
  337. if (it != m_listeners.end())
  338. {
  339. m_listeners.erase(it);
  340. if (m_listeners.empty())
  341. {
  342. m_listeners.shrink_to_fit();
  343. }
  344. }
  345. }
  346. //------------------------------------------------------------------------
  347. bool CLevelSystem::LoadLevel(const char* _levelName)
  348. {
  349. if (gEnv->IsEditor())
  350. {
  351. AZ_TracePrintf("CrySystem::CLevelSystem", "LoadLevel for %s was called in the editor - not actually loading.\n", _levelName);
  352. return false;
  353. }
  354. // If a level is currently loaded, unload it before loading the next one.
  355. if (IsLevelLoaded())
  356. {
  357. UnloadLevel();
  358. }
  359. gEnv->pSystem->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_LOAD_PREPARE, 0, 0);
  360. PrepareNextLevel(_levelName);
  361. ILevel* level = LoadLevelInternal(_levelName);
  362. if (level)
  363. {
  364. OnLoadingComplete(_levelName);
  365. }
  366. return (level != nullptr);
  367. }
  368. //------------------------------------------------------------------------
  369. ILevel* CLevelSystem::LoadLevelInternal(const char* _levelName)
  370. {
  371. gEnv->pSystem->SetSystemGlobalState(ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_START);
  372. CryLog ("Level system is loading \"%s\"", _levelName);
  373. INDENT_LOG_DURING_SCOPE();
  374. char levelName[256];
  375. azstrcpy(levelName, AZ_ARRAY_SIZE(levelName), _levelName);
  376. // Not remove a scope!!!
  377. {
  378. CLevelInfo* pLevelInfo = GetLevelInfoInternal(levelName);
  379. if (!pLevelInfo)
  380. {
  381. // alert the listener
  382. OnLevelNotFound(levelName);
  383. return 0;
  384. }
  385. m_bLevelLoaded = false;
  386. m_lastLevelName = levelName;
  387. delete m_pCurrentLevel;
  388. CLevel* pLevel = new CLevel();
  389. pLevel->m_levelInfo = *pLevelInfo;
  390. m_pCurrentLevel = pLevel;
  391. //////////////////////////////////////////////////////////////////////////
  392. // Read main level info.
  393. if (!pLevelInfo->ReadInfo())
  394. {
  395. OnLoadingError(levelName, "Failed to read level info (level.pak might be corrupted)!");
  396. return 0;
  397. }
  398. //[AlexMcC|19.04.10]: Update the level's LevelInfo
  399. pLevel->m_levelInfo = *pLevelInfo;
  400. //////////////////////////////////////////////////////////////////////////
  401. gEnv->pConsole->SetScrollMax(600);
  402. ICVar* con_showonload = gEnv->pConsole->GetCVar("con_showonload");
  403. if (con_showonload && con_showonload->GetIVal() != 0)
  404. {
  405. gEnv->pConsole->ShowConsole(true);
  406. ICVar* g_enableloadingscreen = gEnv->pConsole->GetCVar("g_enableloadingscreen");
  407. if (g_enableloadingscreen)
  408. {
  409. g_enableloadingscreen->Set(0);
  410. }
  411. }
  412. m_pLoadingLevelInfo = pLevelInfo;
  413. OnLoadingStart(levelName);
  414. auto pPak = gEnv->pCryPak;
  415. AZStd::string levelPath(pLevelInfo->GetPath());
  416. ICVar* pSpamDelay = gEnv->pConsole->GetCVar("log_SpamDelay");
  417. float spamDelay = 0.0f;
  418. if (pSpamDelay)
  419. {
  420. spamDelay = pSpamDelay->GetFVal();
  421. pSpamDelay->Set(0.0f);
  422. }
  423. {
  424. AZStd::string missionXml("mission_");
  425. missionXml += pLevelInfo->m_defaultGameTypeName;
  426. missionXml += ".xml";
  427. AZStd::string xmlFile(pLevelInfo->GetPath());
  428. xmlFile += "/";
  429. xmlFile += missionXml;
  430. if (!gEnv->IsEditor())
  431. {
  432. AZStd::string entitiesFilename =
  433. AZStd::string::format("%s/%s.entities_xml", pLevelInfo->GetPath(), pLevelInfo->m_defaultGameTypeName.c_str());
  434. AZStd::vector<char> fileBuffer;
  435. CCryFile entitiesFile;
  436. if (entitiesFile.Open(entitiesFilename.c_str(), "rt"))
  437. {
  438. fileBuffer.resize(entitiesFile.GetLength());
  439. if (fileBuffer.size() == entitiesFile.ReadRaw(fileBuffer.begin(), fileBuffer.size()))
  440. {
  441. AZ::IO::ByteContainerStream<AZStd::vector<char>> fileStream(&fileBuffer);
  442. EBUS_EVENT(AzFramework::GameEntityContextRequestBus, LoadFromStream, fileStream, false);
  443. }
  444. }
  445. }
  446. }
  447. //////////////////////////////////////////////////////////////////////////
  448. // Movie system must be reset after entities.
  449. //////////////////////////////////////////////////////////////////////////
  450. IMovieSystem* movieSystem = AZ::Interface<IMovieSystem>::Get();
  451. if (movieSystem)
  452. {
  453. // bSeekAllToStart needs to be false here as it's only of interest in the editor
  454. constexpr bool playOnReset = true;
  455. constexpr bool seekToStart = false;
  456. movieSystem->Reset(playOnReset, seekToStart);
  457. }
  458. gEnv->pSystem->SetSystemGlobalState(ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_START_PRECACHE);
  459. //////////////////////////////////////////////////////////////////////////
  460. //////////////////////////////////////////////////////////////////////////
  461. gEnv->pConsole->SetScrollMax(600 / 2);
  462. pPak->GetResourceList(AZ::IO::IArchive::RFOM_NextLevel)->Clear();
  463. if (pSpamDelay)
  464. {
  465. pSpamDelay->Set(spamDelay);
  466. }
  467. m_bLevelLoaded = true;
  468. gEnv->pSystem->SetSystemGlobalState(ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_END);
  469. }
  470. GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_LOAD_END, 0, 0);
  471. if (auto cvar = gEnv->pConsole->GetCVar("sv_map"); cvar)
  472. {
  473. cvar->Set(levelName);
  474. }
  475. gEnv->pSystem->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_PRECACHE_START, 0, 0);
  476. return m_pCurrentLevel;
  477. }
  478. //------------------------------------------------------------------------
  479. void CLevelSystem::PrepareNextLevel(const char* levelName)
  480. {
  481. CLevelInfo* pLevelInfo = GetLevelInfoInternal(levelName);
  482. if (!pLevelInfo)
  483. {
  484. // alert the listener
  485. OnLevelNotFound(levelName);
  486. return;
  487. }
  488. // This work not required in-editor.
  489. if (!gEnv || !gEnv->IsEditor())
  490. {
  491. const AZ::TimeMs timeMs = AZ::GetRealElapsedTimeMs();
  492. const double timeSec = AZ::TimeMsToSecondsDouble(timeMs);
  493. m_levelLoadStartTime = CTimeValue(timeSec);
  494. // Open pak file for a new level.
  495. pLevelInfo->OpenLevelPak();
  496. // switched to level heap, so now imm start the loading screen (renderer will be reinitialized in the levelheap)
  497. gEnv->pSystem->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_LOAD_START_LOADINGSCREEN, 0, 0);
  498. gEnv->pSystem->SetSystemGlobalState(ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_START_PREPARE);
  499. }
  500. for (AZStd::vector<ILevelSystemListener*>::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it)
  501. {
  502. (*it)->OnPrepareNextLevel(pLevelInfo->GetName());
  503. }
  504. AzFramework::LevelSystemLifecycleNotificationBus::Broadcast(
  505. &AzFramework::LevelSystemLifecycleNotifications::OnPrepareNextLevel, levelName);
  506. }
  507. //------------------------------------------------------------------------
  508. void CLevelSystem::OnLevelNotFound(const char* levelName)
  509. {
  510. for (AZStd::vector<ILevelSystemListener*>::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it)
  511. {
  512. (*it)->OnLevelNotFound(levelName);
  513. }
  514. AzFramework::LevelSystemLifecycleNotificationBus::Broadcast(
  515. &AzFramework::LevelSystemLifecycleNotifications::OnLevelNotFound, levelName);
  516. }
  517. //------------------------------------------------------------------------
  518. void CLevelSystem::OnLoadingStart(const char* levelName)
  519. {
  520. if (gEnv->pCryPak->GetRecordFileOpenList() == AZ::IO::IArchive::RFOM_EngineStartup)
  521. {
  522. gEnv->pCryPak->RecordFileOpen(AZ::IO::IArchive::RFOM_Level);
  523. }
  524. const AZ::TimeMs timeMs = AZ::GetRealElapsedTimeMs();
  525. m_fLastTime = AZ::TimeMsToSeconds(timeMs);
  526. GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_LOAD_START, 0, 0);
  527. for (AZStd::vector<ILevelSystemListener*>::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it)
  528. {
  529. (*it)->OnLoadingStart(levelName);
  530. }
  531. AzFramework::LevelSystemLifecycleNotificationBus::Broadcast(
  532. &AzFramework::LevelSystemLifecycleNotifications::OnLoadingStart, levelName);
  533. }
  534. //------------------------------------------------------------------------
  535. void CLevelSystem::OnLoadingError(const char* levelName, const char* error)
  536. {
  537. ILevelInfo* pLevelInfo = m_pLoadingLevelInfo;
  538. if (!pLevelInfo)
  539. {
  540. CRY_ASSERT(false);
  541. return;
  542. }
  543. for (AZStd::vector<ILevelSystemListener*>::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it)
  544. {
  545. (*it)->OnLoadingError(levelName, error);
  546. }
  547. AzFramework::LevelSystemLifecycleNotificationBus::Broadcast(
  548. &AzFramework::LevelSystemLifecycleNotifications::OnLoadingError, levelName, error);
  549. ((CLevelInfo*)pLevelInfo)->CloseLevelPak();
  550. }
  551. //------------------------------------------------------------------------
  552. void CLevelSystem::OnLoadingComplete(const char* levelName)
  553. {
  554. const AZ::TimeMs timeMs = AZ::GetRealElapsedTimeMs();
  555. const double timeSec = AZ::TimeMsToSecondsDouble(timeMs);
  556. const CTimeValue t(timeSec);
  557. m_fLastLevelLoadTime = (t - m_levelLoadStartTime).GetSeconds();
  558. LogLoadingTime();
  559. m_nLoadedLevelsCount++;
  560. // Hide console after loading.
  561. gEnv->pConsole->ShowConsole(false);
  562. for (AZStd::vector<ILevelSystemListener*>::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it)
  563. {
  564. (*it)->OnLoadingComplete(levelName);
  565. }
  566. AzFramework::LevelSystemLifecycleNotificationBus::Broadcast(
  567. &AzFramework::LevelSystemLifecycleNotifications::OnLoadingComplete, levelName);
  568. #if AZ_LOADSCREENCOMPONENT_ENABLED
  569. EBUS_EVENT(LoadScreenBus, Stop);
  570. #endif // if AZ_LOADSCREENCOMPONENT_ENABLED
  571. }
  572. //------------------------------------------------------------------------
  573. void CLevelSystem::OnLoadingProgress(const char* levelName, int progressAmount)
  574. {
  575. for (AZStd::vector<ILevelSystemListener*>::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it)
  576. {
  577. (*it)->OnLoadingProgress(levelName, progressAmount);
  578. }
  579. AzFramework::LevelSystemLifecycleNotificationBus::Broadcast(
  580. &AzFramework::LevelSystemLifecycleNotifications::OnLoadingProgress, levelName, progressAmount);
  581. }
  582. //------------------------------------------------------------------------
  583. void CLevelSystem::OnUnloadComplete(const char* levelName)
  584. {
  585. for (AZStd::vector<ILevelSystemListener*>::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it)
  586. {
  587. (*it)->OnUnloadComplete(levelName);
  588. }
  589. AzFramework::LevelSystemLifecycleNotificationBus::Broadcast(
  590. &AzFramework::LevelSystemLifecycleNotifications::OnUnloadComplete, levelName);
  591. }
  592. //////////////////////////////////////////////////////////////////////////
  593. void CLevelSystem::LogLoadingTime()
  594. {
  595. if (gEnv->IsEditor())
  596. {
  597. return;
  598. }
  599. if (!GetISystem()->IsDevMode())
  600. {
  601. return;
  602. }
  603. char vers[128];
  604. GetISystem()->GetFileVersion().ToString(vers, sizeof(vers));
  605. const char* sChain = "";
  606. if (m_nLoadedLevelsCount > 0)
  607. {
  608. sChain = " (Chained)";
  609. }
  610. gEnv->pLog->Log("Game Level Load Time: [%s] Level %s loaded in %.2f seconds%s", vers, m_lastLevelName.c_str(), m_fLastLevelLoadTime, sChain);
  611. }
  612. //////////////////////////////////////////////////////////////////////////
  613. void CLevelSystem::UnloadLevel()
  614. {
  615. if (gEnv->IsEditor())
  616. {
  617. return;
  618. }
  619. if (!m_pLoadingLevelInfo)
  620. {
  621. return;
  622. }
  623. CryLog("UnloadLevel Start");
  624. INDENT_LOG_DURING_SCOPE();
  625. // Flush core buses. We're about to unload Cry modules and need to ensure we don't have module-owned functions left behind.
  626. AZ::Data::AssetBus::ExecuteQueuedEvents();
  627. AZ::TickBus::ExecuteQueuedEvents();
  628. AZ::MainThreadRenderRequestBus::ExecuteQueuedEvents();
  629. if (gEnv && gEnv->pSystem)
  630. {
  631. // clear all error messages to prevent stalling due to runtime file access check during chainloading
  632. gEnv->pSystem->ClearErrorMessages();
  633. }
  634. if (gEnv && gEnv->pCryPak)
  635. {
  636. gEnv->pCryPak->DisableRuntimeFileAccess(false);
  637. }
  638. const AZ::TimeMs beginTimeMs = AZ::GetRealElapsedTimeMs();
  639. // Clear level entities and prefab instances.
  640. EBUS_EVENT(AzFramework::GameEntityContextRequestBus, ResetGameContext);
  641. IMovieSystem* movieSystem = AZ::Interface<IMovieSystem>::Get();
  642. if (movieSystem)
  643. {
  644. constexpr bool playOnReset = false;
  645. constexpr bool seekToStart = false;
  646. movieSystem->Reset(playOnReset, seekToStart);
  647. movieSystem->RemoveAllSequences();
  648. }
  649. OnUnloadComplete(m_lastLevelName.c_str());
  650. // -- kenzo: this will close all pack files for this level
  651. // (even the ones which were not added through here, if this is not desired,
  652. // then change code to close only level.pak)
  653. if (m_pLoadingLevelInfo)
  654. {
  655. ((CLevelInfo*)m_pLoadingLevelInfo)->CloseLevelPak();
  656. m_pLoadingLevelInfo = NULL;
  657. }
  658. m_lastLevelName.clear();
  659. SAFE_RELEASE(m_pCurrentLevel);
  660. // Force Lua garbage collection (may no longer be needed now the legacy renderer has been removed).
  661. // Normally the GC step is triggered at the end of this method (by the ESYSTEM_EVENT_LEVEL_POST_UNLOAD event).
  662. EBUS_EVENT(AZ::ScriptSystemRequestBus, GarbageCollect);
  663. m_bLevelLoaded = false;
  664. [[maybe_unused]] const AZ::TimeMs unloadTimeMs = AZ::GetRealElapsedTimeMs() - beginTimeMs;
  665. CryLog("UnloadLevel End: %.1f sec", AZ::TimeMsToSeconds(unloadTimeMs));
  666. // Must be sent last.
  667. // Cleanup all containers
  668. GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_POST_UNLOAD, 0, 0);
  669. AzFramework::InputChannelRequestBus::Broadcast(&AzFramework::InputChannelRequests::ResetState);
  670. }
  671. } // namespace LegacyLevelSystem