LevelSystem.cpp 29 KB

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