LevelSystem.cpp 29 KB

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