/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. #include "StdAfx.h" #include "LevelSystem.h" #include #include "IMovieSystem.h" #include "IDialogSystem.h" #include "IMaterialEffects.h" #include #include #include "IDeferredCollisionEvent.h" #include "IPlatformOS.h" #include #include #include #include #include #include #include "MainThreadRenderRequestBus.h" #include #include #include #include #ifdef WIN32 #include #endif namespace LegacyLevelSystem { int CLevelSystem::s_loadCount = 0; //------------------------------------------------------------------------ bool CLevelInfo::SupportsGameType(const char* gameTypeName) const { //read level meta data for (int i = 0; i < m_gamerules.size(); ++i) { if (!azstricmp(m_gamerules[i].c_str(), gameTypeName)) { return true; } } return false; } //------------------------------------------------------------------------ const char* CLevelInfo::GetDisplayName() const { return m_levelDisplayName.c_str(); } ////////////////////////////////////////////////////////////////////////// bool CLevelInfo::ReadInfo() { string levelPath = m_levelPath; string xmlFile = levelPath + string("/LevelInfo.xml"); XmlNodeRef rootNode = GetISystem()->LoadXmlFromFile(xmlFile.c_str()); if (rootNode) { m_heightmapSize = atoi(rootNode->getAttr("HeightmapSize")); string dataFile = levelPath + string("/LevelDataAction.xml"); XmlNodeRef dataNode = GetISystem()->LoadXmlFromFile(dataFile.c_str()); if (!dataNode) { dataFile = levelPath + string("/LevelData.xml"); dataNode = GetISystem()->LoadXmlFromFile(dataFile.c_str()); } if (dataNode) { XmlNodeRef gameTypesNode = dataNode->findChild("Missions"); if ((gameTypesNode != 0) && (gameTypesNode->getChildCount() > 0)) { m_gameTypes.clear(); //m_musicLibs.clear(); for (int i = 0; i < gameTypesNode->getChildCount(); i++) { XmlNodeRef gameTypeNode = gameTypesNode->getChild(i); if (gameTypeNode->isTag("Mission")) { const char* gameTypeName = gameTypeNode->getAttr("Name"); if (gameTypeName) { ILevelInfo::TGameTypeInfo info; info.cgfCount = 0; gameTypeNode->getAttr("CGFCount", info.cgfCount); info.name = gameTypeNode->getAttr("Name"); info.xmlFile = gameTypeNode->getAttr("File"); m_gameTypes.push_back(info); } } } // Gets reintroduced when level specific music data loading is implemented. /*XmlNodeRef musicLibraryNode = dataNode->findChild("MusicLibrary"); if ((musicLibraryNode!=0) && (musicLibraryNode->getChildCount() > 0)) { for (int i = 0; i < musicLibraryNode->getChildCount(); i++) { XmlNodeRef musicLibrary = musicLibraryNode->getChild(i); if (musicLibrary->isTag("Library")) { const char *musicLibraryName = musicLibrary->getAttr("File"); if (musicLibraryName) { m_musicLibs.push_back(string("music/") + musicLibraryName); } } } }*/ } } } return rootNode != 0; } ////////////////////////////////////////////////////////////////////////// void CLevelInfo::ReadMetaData() { string fullPath(GetPath()); int slashPos = fullPath.find_last_of("\\/"); string mapName = fullPath.substr(slashPos + 1, fullPath.length() - slashPos); fullPath.append("/"); fullPath.append(mapName); fullPath.append(".xml"); m_levelDisplayName = string("@ui_") + mapName; if (!gEnv->pCryPak->IsFileExist(fullPath.c_str())) { return; } XmlNodeRef mapInfo = GetISystem()->LoadXmlFromFile(fullPath.c_str()); //retrieve the coordinates of the map bool foundMinimapInfo = false; if (mapInfo) { for (int n = 0; n < mapInfo->getChildCount(); ++n) { XmlNodeRef rulesNode = mapInfo->getChild(n); const char* name = rulesNode->getTag(); if (!azstricmp(name, "Gamerules")) { for (int a = 0; a < rulesNode->getNumAttributes(); ++a) { const char* key, * value; rulesNode->getAttributeByIndex(a, &key, &value); m_gamerules.push_back(value); } } else if (!azstricmp(name, "Display")) { XmlString v; if (rulesNode->getAttr("Name", v)) { m_levelDisplayName = v.c_str(); } } else if (!azstricmp(name, "PreviewImage")) { const char* pFilename = NULL; if (rulesNode->getAttr("Filename", &pFilename)) { m_previewImagePath = pFilename; } } else if (!azstricmp(name, "BackgroundImage")) { const char* pFilename = NULL; if (rulesNode->getAttr("Filename", &pFilename)) { m_backgroundImagePath = pFilename; } } else if (!azstricmp(name, "Minimap")) { foundMinimapInfo = true; const char* minimap_dds = ""; foundMinimapInfo &= rulesNode->getAttr("Filename", &minimap_dds); m_minimapImagePath = minimap_dds; m_minimapInfo.sMinimapName = GetPath(); m_minimapInfo.sMinimapName.append("/"); m_minimapInfo.sMinimapName.append(minimap_dds); foundMinimapInfo &= rulesNode->getAttr("startX", m_minimapInfo.fStartX); foundMinimapInfo &= rulesNode->getAttr("startY", m_minimapInfo.fStartY); foundMinimapInfo &= rulesNode->getAttr("endX", m_minimapInfo.fEndX); foundMinimapInfo &= rulesNode->getAttr("endY", m_minimapInfo.fEndY); foundMinimapInfo &= rulesNode->getAttr("width", m_minimapInfo.iWidth); foundMinimapInfo &= rulesNode->getAttr("height", m_minimapInfo.iHeight); m_minimapInfo.fDimX = m_minimapInfo.fEndX - m_minimapInfo.fStartX; m_minimapInfo.fDimY = m_minimapInfo.fEndY - m_minimapInfo.fStartY; m_minimapInfo.fDimX = m_minimapInfo.fDimX > 0 ? m_minimapInfo.fDimX : 1; m_minimapInfo.fDimY = m_minimapInfo.fDimY > 0 ? m_minimapInfo.fDimY : 1; } else if (!azstricmp(name, "Tag")) { m_levelTag = ILevelSystem::TAG_UNKNOWN; SwapEndian(m_levelTag, eBigEndian); const char* pTag = NULL; if (rulesNode->getAttr("Value", &pTag)) { m_levelTag = 0; memcpy(&m_levelTag, pTag, std::min(sizeof(m_levelTag), strlen(pTag))); } } else if (!azstricmp(name, "LevelType")) { const char* levelType; if (rulesNode->getAttr("value", &levelType)) { m_levelTypeList.push_back(levelType); } } } m_bMetaDataRead = true; } if (!foundMinimapInfo) { gEnv->pLog->LogWarning("Map %s: Missing or invalid minimap info!", mapName.c_str()); } } //------------------------------------------------------------------------ const ILevelInfo::TGameTypeInfo* CLevelInfo::GetDefaultGameType() const { if (!m_gameTypes.empty()) { return &m_gameTypes[0]; } return 0; }; /// Used by console auto completion. struct SLevelNameAutoComplete : public IConsoleArgumentAutoComplete { std::vector levels; virtual int GetCount() const { return levels.size(); }; virtual const char* GetValue(int nIndex) const { return levels[nIndex].c_str(); }; }; // definition and declaration must be separated for devirtualization static StaticInstance> g_LevelNameAutoComplete; //------------------------------------------------------------------------ static void LoadMap(IConsoleCmdArgs* args) { if (gEnv->pSystem && gEnv->pSystem->GetILevelSystem() && !gEnv->IsEditor()) { gEnv->pSystem->GetILevelSystem()->UnLoadLevel(); gEnv->pSystem->GetILevelSystem()->LoadLevel(args->GetArg(1)); } } //------------------------------------------------------------------------ static void UnloadMap(IConsoleCmdArgs* args) { if (gEnv->pSystem && gEnv->pSystem->GetILevelSystem() && !gEnv->IsEditor()) { gEnv->pSystem->GetILevelSystem()->UnLoadLevel(); if (gEnv->p3DEngine) { gEnv->p3DEngine->LoadEmptyLevel(); } } } //------------------------------------------------------------------------ CLevelSystem::CLevelSystem(ISystem* pSystem, const char* levelsFolder) : m_pSystem(pSystem) , m_pCurrentLevel(0) , m_pLoadingLevelInfo(0) { LOADING_TIME_PROFILE_SECTION; CRY_ASSERT(pSystem); //Load user defined level types if (XmlNodeRef levelTypeNode = m_pSystem->LoadXmlFromFile("Libs/Levels/leveltypes.xml")) { for (unsigned int i = 0; i < levelTypeNode->getChildCount(); ++i) { XmlNodeRef child = levelTypeNode->getChild(i); const char* levelType; if (child->getAttr("value", &levelType)) { m_levelTypeList.push_back(string(levelType)); } } } //if (!gEnv->IsEditor()) Rescan(levelsFolder, ILevelSystem::TAG_MAIN); m_fLastLevelLoadTime = 0; m_fFilteredProgress = 0; m_fLastTime = 0; m_bLevelLoaded = false; m_bRecordingFileOpens = false; m_levelLoadStartTime.SetValue(0); m_nLoadedLevelsCount = 0; REGISTER_COMMAND("map", LoadMap, VF_BLOCKFRAME, "Load a map"); REGISTER_COMMAND("unload", UnloadMap, 0, "Unload current map"); gEnv->pConsole->RegisterAutoComplete("map", &(*g_LevelNameAutoComplete)); } //------------------------------------------------------------------------ CLevelSystem::~CLevelSystem() { } //------------------------------------------------------------------------ void CLevelSystem::Rescan(const char* levelsFolder, const uint32 tag) { if (levelsFolder) { if (const ICmdLineArg* pModArg = m_pSystem->GetICmdLine()->FindArg(eCLAT_Pre, "MOD")) { if (m_pSystem->IsMODValid(pModArg->GetValue())) { m_levelsFolder.Format("Mods/%s/%s", pModArg->GetValue(), levelsFolder); m_levelsFolder.clear(); ScanFolder(0, true, tag); } } m_levelsFolder = levelsFolder; } CRY_ASSERT(!m_levelsFolder.empty()); m_levelsFolder.clear(); m_levelInfos.reserve(64); ScanFolder(0, false, tag); g_LevelNameAutoComplete->levels.clear(); for (int i = 0; i < (int)m_levelInfos.size(); i++) { g_LevelNameAutoComplete->levels.push_back(PathUtil::GetFileName(m_levelInfos[i].GetName())); } } //----------------------------------------------------------------------- void CLevelSystem::PopulateLevels(string searchPattern, string& folder, ICryPak* pPak, bool& modFolder, const uint32& tag, bool fromFileSystemOnly) { _finddata_t fd; intptr_t handle = 0; //CryLog("[DLC] ScanFolder: search:'%s'", search.c_str()); // --kenzo // allow this find first to actually touch the file system // (causes small overhead but with minimal amount of levels this should only be around 150ms on actual DVD Emu) handle = pPak->FindFirst(searchPattern.c_str(), &fd, 0, fromFileSystemOnly); if (handle > -1) { do { if (!(fd.attrib & _A_SUBDIR) || !strcmp(fd.name, ".") || !strcmp(fd.name, "..")) { continue; } CLevelInfo levelInfo; string levelFolder = (folder.empty() ? "" : (folder + "/")) + string(fd.name); string levelPath = m_levelsFolder + "/" + levelFolder; string paks = levelPath + string("/*.pak"); //CryLog("[DLC] ScanFolder fd:'%s' levelPath:'%s'", fd.name, levelPath.c_str()); const string levelPakName = levelPath + "/level.pak"; const string levelInfoName = levelPath + "/levelinfo.xml"; if (!pPak->IsFileExist(levelPakName.c_str(), ICryPak::eFileLocation_OnDisk) && !pPak->IsFileExist(levelInfoName.c_str())) { ScanFolder(levelFolder.c_str(), modFolder, tag); continue; } //CryLog("[DLC] ScanFolder adding level:'%s'", levelPath.c_str()); levelInfo.m_levelPath = levelPath; levelInfo.m_levelPaks = paks; levelInfo.m_levelName = levelFolder; levelInfo.m_levelName = UnifyName(levelInfo.m_levelName); levelInfo.m_isModLevel = modFolder; levelInfo.m_scanTag = tag; levelInfo.m_levelTag = ILevelSystem::TAG_UNKNOWN; SwapEndian(levelInfo.m_scanTag, eBigEndian); SwapEndian(levelInfo.m_levelTag, eBigEndian); CLevelInfo* pExistingInfo = GetLevelInfoInternal(levelInfo.m_levelName); if (pExistingInfo && pExistingInfo->MetadataLoaded() == false) { //Reload metadata if it failed to load pExistingInfo->ReadMetaData(); } // Don't add the level if it is already in the list if (pExistingInfo == NULL) { levelInfo.ReadMetaData(); m_levelInfos.push_back(levelInfo); } else { // Update the scan tag pExistingInfo->m_scanTag = tag; } } while (pPak->FindNext(handle, &fd) >= 0); pPak->FindClose(handle); } } //------------------------------------------------------------------------ void CLevelSystem::ScanFolder(const char* subfolder, bool modFolder, const uint32 tag) { string folder; if (subfolder && subfolder[0]) { folder = subfolder; } string search(m_levelsFolder); if (!folder.empty()) { search += string("/") + folder; } search += AZ_FILESYSTEM_SEPARATOR_WILDCARD; ICryPak* pPak = gEnv->pCryPak; PopulateLevels(search, folder, pPak, modFolder, tag, true); } //------------------------------------------------------------------------ int CLevelSystem::GetLevelCount() { return (int)m_levelInfos.size(); } //------------------------------------------------------------------------ ILevelInfo* CLevelSystem::GetLevelInfo(int level) { return GetLevelInfoInternal(level); } //------------------------------------------------------------------------ CLevelInfo* CLevelSystem::GetLevelInfoInternal(int level) { if ((level >= 0) && (level < GetLevelCount())) { return &m_levelInfos[level]; } return 0; } //------------------------------------------------------------------------ ILevelInfo* CLevelSystem::GetLevelInfo(const char* levelName) { return GetLevelInfoInternal(levelName); } //------------------------------------------------------------------------ CLevelInfo* CLevelSystem::GetLevelInfoInternal(const char* levelName) { // If level not found by full name try comparing with only filename for (std::vector::iterator it = m_levelInfos.begin(); it != m_levelInfos.end(); ++it) { if (!azstricmp(it->GetName(), levelName)) { return &(*it); } } ////////////////////////////////////////////////////////////////////////// for (std::vector::iterator it = m_levelInfos.begin(); it != m_levelInfos.end(); ++it) { if (!azstricmp(PathUtil::GetFileName(it->GetName()), levelName)) { return &(*it); } } // Try stripping out the folder to find the raw filename string sLevelName(levelName); size_t lastSlash = sLevelName.find_last_of('\\'); if (lastSlash == string::npos) { lastSlash = sLevelName.find_last_of('/'); } if (lastSlash != string::npos) { sLevelName = sLevelName.substr(lastSlash + 1, sLevelName.size() - lastSlash - 1); return GetLevelInfoInternal(sLevelName.c_str()); } return 0; } //------------------------------------------------------------------------ void CLevelSystem::AddListener(ILevelSystemListener* pListener) { std::vector::iterator it = std::find(m_listeners.begin(), m_listeners.end(), pListener); if (it == m_listeners.end()) { m_listeners.reserve(12); m_listeners.push_back(pListener); } } //------------------------------------------------------------------------ void CLevelSystem::RemoveListener(ILevelSystemListener* pListener) { std::vector::iterator it = std::find(m_listeners.begin(), m_listeners.end(), pListener); if (it != m_listeners.end()) { m_listeners.erase(it); if (m_listeners.empty()) { stl::free_container(m_listeners); } } } //------------------------------------------------------------------------ ILevel* CLevelSystem::LoadLevel(const char* _levelName) { if (gEnv->IsEditor()) { AZ_TracePrintf("CrySystem::CLevelSystem", "LoadLevel for %s was called in the editor - not actually loading.\n", _levelName); return nullptr; } gEnv->pSystem->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_LOAD_PREPARE, 0, 0); PrepareNextLevel(_levelName); ILevel* level = LoadLevelInternal(_levelName); if (level) { OnLoadingComplete(level); // the editor sends these same events when you tell it to start running the actual game (Ctrl+G) if (!gEnv->IsEditor() && gEnv->pEntitySystem) { SEntityEvent entityEvent; entityEvent.event = ENTITY_EVENT_START_LEVEL; gEnv->pEntitySystem->SendEventToAll(entityEvent); entityEvent.event = ENTITY_EVENT_START_GAME; gEnv->pEntitySystem->SendEventToAll(entityEvent); } } return level; } //------------------------------------------------------------------------ ILevel* CLevelSystem::LoadLevelInternal(const char* _levelName) { gEnv->pSystem->SetSystemGlobalState(ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_START); AZ_ASSET_NAMED_SCOPE("Level: %s", _levelName); CryLog ("Level system is loading \"%s\"", _levelName); INDENT_LOG_DURING_SCOPE(); char levelName[256]; cry_strcpy(levelName, _levelName); // Not remove a scope!!! { LOADING_TIME_PROFILE_SECTION; //m_levelLoadStartTime = gEnv->pTimer->GetAsyncTime(); CLevelInfo* pLevelInfo = GetLevelInfoInternal(levelName); if (!pLevelInfo) { // alert the listener OnLevelNotFound(levelName); return 0; } m_bLevelLoaded = false; const bool bLoadingSameLevel = m_lastLevelName.compareNoCase(levelName) == 0; m_lastLevelName = levelName; delete m_pCurrentLevel; CLevel* pLevel = new CLevel(); pLevel->m_levelInfo = *pLevelInfo; m_pCurrentLevel = pLevel; ////////////////////////////////////////////////////////////////////////// // Read main level info. if (!pLevelInfo->ReadInfo()) { OnLoadingError(pLevelInfo, "Failed to read level info (level.pak might be corrupted)!"); return 0; } //[AlexMcC|19.04.10]: Update the level's LevelInfo pLevel->m_levelInfo = *pLevelInfo; ////////////////////////////////////////////////////////////////////////// gEnv->pConsole->SetScrollMax(600); ICVar* con_showonload = gEnv->pConsole->GetCVar("con_showonload"); if (con_showonload && con_showonload->GetIVal() != 0) { gEnv->pConsole->ShowConsole(true); ICVar* g_enableloadingscreen = gEnv->pConsole->GetCVar("g_enableloadingscreen"); if (g_enableloadingscreen) { g_enableloadingscreen->Set(0); } } // Reset the camera to (1,1,1) (not (0,0,0) which is the invalid/uninitialised state, // to avoid the hack in the renderer to not show anything if the camera is at the origin). CCamera defaultCam; defaultCam.SetPosition(Vec3(1.0f)); m_pSystem->SetViewCamera(defaultCam); m_pLoadingLevelInfo = pLevelInfo; OnLoadingStart(pLevelInfo); #if ENABLE_CRY_PHYSICS // ensure a physical global area is present IPhysicalWorld* pPhysicalWorld = gEnv->pPhysicalWorld; pPhysicalWorld->AddGlobalArea(); #endif // ENABLE_CRY_PHYSICS ICryPak* pPak = gEnv->pCryPak; string levelPath = pLevelInfo->GetPath(); /* ICVar *pFileCache = gEnv->pConsole->GetCVar("sys_FileCache"); CRY_ASSERT(pFileCache); if(pFileCache->GetIVal()) { if(pPak->OpenPack("",pLevelInfo->GetPath()+string("/FileCache.dat"))) gEnv->pLog->Log("FileCache.dat loaded"); else gEnv->pLog->Log("FileCache.dat not loaded"); } */ m_pSystem->SetThreadState(ESubsys_Physics, false); ICVar* pSpamDelay = gEnv->pConsole->GetCVar("log_SpamDelay"); float spamDelay = 0.0f; if (pSpamDelay) { spamDelay = pSpamDelay->GetFVal(); pSpamDelay->Set(0.0f); } if (gEnv->pEntitySystem) { // load layer infos before load level by 3dEngine and EntitySystem gEnv->pEntitySystem->LoadLayers(pLevelInfo->GetPath() + string("/LevelData.xml")); } bool is3DEngineLoaded = gEnv->IsEditor() ? gEnv->p3DEngine->InitLevelForEditor(pLevelInfo->GetPath(), pLevelInfo->GetDefaultGameType()->name) : gEnv->p3DEngine->LoadLevel(pLevelInfo->GetPath(), pLevelInfo->GetDefaultGameType()->name); if (!is3DEngineLoaded) { OnLoadingError(pLevelInfo, "3DEngine failed to handle loading the level"); return 0; } OnLoadingLevelEntitiesStart(pLevelInfo); gEnv->pSystem->SetSystemGlobalState(ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_START_ENTITIES); if (gEnv->pEntitySystem && !gEnv->pEntitySystem->OnLoadLevel(pLevelInfo->GetPath())) { OnLoadingError(pLevelInfo, "EntitySystem failed to handle loading the level"); return 0; } // reset all the script timers if (gEnv->pScriptSystem) { gEnv->pScriptSystem->ResetTimers(); } if (gEnv->pAISystem) { gEnv->pAISystem->Reset(IAISystem::RESET_LOAD_LEVEL); } // Reset dialog system if (gEnv->pDialogSystem) { gEnv->pDialogSystem->Reset(false); gEnv->pDialogSystem->Init(); } // Parse level specific config data. string const sLevelNameOnly = PathUtil::GetFileName(levelName); if (!sLevelNameOnly.empty() && sLevelNameOnly.compareNoCase("Untitled") != 0) { const char* controlsPath = nullptr; Audio::AudioSystemRequestBus::BroadcastResult(controlsPath, &Audio::AudioSystemRequestBus::Events::GetControlsPath); if (controlsPath) { AZStd::string sAudioLevelPath(controlsPath); sAudioLevelPath.append("levels/"); sAudioLevelPath += sLevelNameOnly; Audio::SAudioManagerRequestData oAMData(sAudioLevelPath.c_str(), Audio::eADS_LEVEL_SPECIFIC); Audio::SAudioRequest oAudioRequestData; oAudioRequestData.nFlags = (Audio::eARF_PRIORITY_HIGH | Audio::eARF_EXECUTE_BLOCKING); // Needs to be blocking so data is available for next preloading request! oAudioRequestData.pData = &oAMData; Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequestBlocking, oAudioRequestData); Audio::SAudioManagerRequestData oAMData2(sAudioLevelPath.c_str(), Audio::eADS_LEVEL_SPECIFIC); oAudioRequestData.pData = &oAMData2; Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequestBlocking, oAudioRequestData); Audio::TAudioPreloadRequestID nPreloadRequestID = INVALID_AUDIO_PRELOAD_REQUEST_ID; Audio::AudioSystemRequestBus::BroadcastResult(nPreloadRequestID, &Audio::AudioSystemRequestBus::Events::GetAudioPreloadRequestID, sLevelNameOnly.c_str()); if (nPreloadRequestID != INVALID_AUDIO_PRELOAD_REQUEST_ID) { Audio::SAudioManagerRequestData requestData(nPreloadRequestID, true); oAudioRequestData.pData = &requestData; Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequestBlocking, oAudioRequestData); } } } if (gEnv->pAISystem && gEnv->pAISystem->IsEnabled()) { gEnv->pAISystem->FlushSystem(); gEnv->pAISystem->LoadLevelData(pLevelInfo->GetPath(), pLevelInfo->GetDefaultGameType()->name); } if (gEnv->pEntitySystem) { gEnv->pEntitySystem->ReserveEntityId(1); gEnv->pEntitySystem->ReserveEntityId(LOCAL_PLAYER_ENTITY_ID); } string missionXml = pLevelInfo->GetDefaultGameType()->xmlFile; string xmlFile = string(pLevelInfo->GetPath()) + "/" + missionXml; XmlNodeRef rootNode = m_pSystem->LoadXmlFromFile(xmlFile.c_str()); if (rootNode) { INDENT_LOG_DURING_SCOPE (true, "Reading '%s'", xmlFile.c_str()); const char* script = rootNode->getAttr("Script"); if (script && script[0] && gEnv->pScriptSystem) { CryLog ("Executing script '%s'", script); INDENT_LOG_DURING_SCOPE(); gEnv->pScriptSystem->ExecuteFile(script, true, true); } XmlNodeRef objectsNode = rootNode->findChild("Objects"); if (objectsNode && gEnv->pEntitySystem) { gEnv->pEntitySystem->LoadEntities(objectsNode, true); } } if (!gEnv->IsEditor()) { AZStd::string entitiesFilename = AZStd::string::format("%s/%s.entities_xml", pLevelInfo->GetPath(), pLevelInfo->GetDefaultGameType()->name.c_str()); AZStd::vector fileBuffer; CCryFile entitiesFile; if (entitiesFile.Open(entitiesFilename.c_str(), "rt")) { fileBuffer.resize(entitiesFile.GetLength()); if (fileBuffer.size() == entitiesFile.ReadRaw(fileBuffer.begin(), fileBuffer.size())) { AZ::IO::ByteContainerStream > fileStream(&fileBuffer); EBUS_EVENT(AzFramework::GameEntityContextRequestBus, LoadFromStream, fileStream, false); } } } // Now that we've registered our AI objects, we can init if (gEnv->pAISystem) { gEnv->pAISystem->Reset(IAISystem::RESET_ENTER_GAME); } ////////////////////////////////////////////////////////////////////////// // Movie system must be reset after entities. ////////////////////////////////////////////////////////////////////////// IMovieSystem* movieSys = gEnv->pMovieSystem; if (movieSys != NULL) { // bSeekAllToStart needs to be false here as it's only of interest in the editor movieSys->Reset(true, false); } gEnv->pSystem->SetSystemGlobalState(ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_START_PRECACHE); ////////////////////////////////////////////////////////////////////////// // Notify 3D engine that loading finished ////////////////////////////////////////////////////////////////////////// gEnv->p3DEngine->PostLoadLevel(); if (gEnv->pScriptSystem) { // After level was loaded force GC cycle in Lua gEnv->pScriptSystem->ForceGarbageCollection(); } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// gEnv->pConsole->SetScrollMax(600 / 2); pPak->GetResourceList(ICryPak::RFOM_NextLevel)->Clear(); if (pSpamDelay) { pSpamDelay->Set(spamDelay); } m_bLevelLoaded = true; gEnv->pSystem->SetSystemGlobalState(ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_END); } GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_LOAD_END, 0, 0); gEnv->pSystem->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_PRECACHE_START, 0, 0); if (!gEnv->pSystem->IsSerializingFile() && gEnv->pEntitySystem) { // activate the default layers gEnv->pEntitySystem->EnableDefaultLayers(); } m_pSystem->SetThreadState(ESubsys_Physics, true); return m_pCurrentLevel; } //------------------------------------------------------------------------ ILevel* CLevelSystem::SetEditorLoadedLevel(const char* levelName, bool bReadLevelInfoMetaData) { CLevelInfo* pLevelInfo = GetLevelInfoInternal(levelName); if (!pLevelInfo) { gEnv->pLog->LogError("Failed to get level info for level %s!", levelName); return 0; } if (bReadLevelInfoMetaData) { pLevelInfo->ReadMetaData(); } m_lastLevelName = levelName; SAFE_DELETE(m_pCurrentLevel); CLevel* pLevel = new CLevel(); pLevel->m_levelInfo = *pLevelInfo; m_pCurrentLevel = pLevel; m_bLevelLoaded = true; return m_pCurrentLevel; } //------------------------------------------------------------------------ // Search positions of all entities with class "PrecacheCamera" and pass it to the 3dengine void CLevelSystem::PrecacheLevelRenderData() { if (gEnv->IsDedicated()) { return; } // gEnv->pSystem->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_PRECACHE_START, NULL, NULL); #ifndef CONSOLE_CONST_CVAR_MODE ICVar* pPrecacheVar = gEnv->pConsole->GetCVar("e_PrecacheLevel"); CRY_ASSERT(pPrecacheVar); if (pPrecacheVar && pPrecacheVar->GetIVal() > 0 && gEnv->pEntitySystem) { { if (I3DEngine* p3DEngine = gEnv->p3DEngine) { std::vector arrCamOutdoorPositions; IEntitySystem* pEntitySystem = gEnv->pEntitySystem; IEntityItPtr pEntityIter = pEntitySystem->GetEntityIterator(); IEntityClass* pPrecacheCameraClass = pEntitySystem->GetClassRegistry()->FindClass("PrecacheCamera"); IEntity* pEntity = NULL; while (pEntity = pEntityIter->Next()) { if (pEntity->GetClass() == pPrecacheCameraClass) { arrCamOutdoorPositions.push_back(pEntity->GetWorldPos()); } } Vec3* pPoints = 0; if (arrCamOutdoorPositions.size() > 0) { pPoints = &arrCamOutdoorPositions[0]; } p3DEngine->PrecacheLevel(true, pPoints, arrCamOutdoorPositions.size()); } } } #endif // gEnv->pSystem->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_PRECACHE_END, NULL, NULL); } //------------------------------------------------------------------------ void CLevelSystem::PrepareNextLevel(const char* levelName) { CLevelInfo* pLevelInfo = GetLevelInfoInternal(levelName); if (!pLevelInfo) { // alert the listener OnLevelNotFound(levelName); return; } // This work not required in-editor. if (!gEnv || !gEnv->IsEditor()) { m_levelLoadStartTime = gEnv->pTimer->GetAsyncTime(); // force a Lua deep garbage collection if (gEnv->pScriptSystem) { gEnv->pScriptSystem->ForceGarbageCollection(); } // Open pak file for a new level. pLevelInfo->OpenLevelPak(); // switched to level heap, so now imm start the loading screen (renderer will be reinitialized in the levelheap) gEnv->pSystem->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_LOAD_START_LOADINGSCREEN, (UINT_PTR)pLevelInfo, 0); gEnv->pSystem->SetSystemGlobalState(ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_START_PREPARE); // Inform resource manager about loading of the new level. GetISystem()->GetIResourceManager()->PrepareLevel(pLevelInfo->GetPath(), pLevelInfo->GetName()); // Disable locking of resources to allow everything to be offloaded. //string filename = PathUtil::Make( pLevelInfo->GetPath(),"resourcelist.txt" ); //gEnv->pCryPak->GetResourceList(ICryPak::RFOM_NextLevel)->Load( filename.c_str() ); } for (std::vector::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) { (*it)->OnPrepareNextLevel(pLevelInfo); } } //------------------------------------------------------------------------ void CLevelSystem::OnLevelNotFound(const char* levelName) { for (std::vector::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) { (*it)->OnLevelNotFound(levelName); } } //------------------------------------------------------------------------ void CLevelSystem::OnLoadingStart(ILevelInfo* pLevelInfo) { if (gEnv->pCryPak->GetRecordFileOpenList() == ICryPak::RFOM_EngineStartup) { gEnv->pCryPak->RecordFileOpen(ICryPak::RFOM_Level); } m_fFilteredProgress = 0.f; m_fLastTime = gEnv->pTimer->GetAsyncCurTime(); GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_LOAD_START, 0, 0); LOADING_TIME_PROFILE_SECTION(gEnv->pSystem); #ifdef WIN32 /* m_bRecordingFileOpens = GetISystem()->IsDevMode() && gEnv->pCryPak->GetRecordFileOpenList() == ICryPak::RFOM_Disabled; if (m_bRecordingFileOpens) { gEnv->pCryPak->GetResourceList(ICryPak::RFOM_Level)->Clear(); gEnv->pCryPak->RecordFileOpen(ICryPak::RFOM_Level); } */ #endif for (std::vector::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) { (*it)->OnLoadingStart(pLevelInfo); } } //------------------------------------------------------------------------ void CLevelSystem::OnLoadingLevelEntitiesStart(ILevelInfo* pLevelInfo) { for (std::vector::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) { (*it)->OnLoadingLevelEntitiesStart(pLevelInfo); } } //------------------------------------------------------------------------ void CLevelSystem::OnLoadingError(ILevelInfo* pLevelInfo, const char* error) { if (!pLevelInfo) { pLevelInfo = m_pLoadingLevelInfo; } if (!pLevelInfo) { CRY_ASSERT(false); return; } if (gEnv->pRenderer) { gEnv->pRenderer->SetTexturePrecaching(false); } for (std::vector::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) { (*it)->OnLoadingError(pLevelInfo, error); } ((CLevelInfo*)pLevelInfo)->CloseLevelPak(); } //------------------------------------------------------------------------ void CLevelSystem::OnLoadingComplete(ILevel* pLevelInfo) { PrecacheLevelRenderData(); if (m_bRecordingFileOpens) { // Stop recoding file opens. gEnv->pCryPak->RecordFileOpen(ICryPak::RFOM_Disabled); // Save recorded list. SaveOpenedFilesList(); } CTimeValue t = gEnv->pTimer->GetAsyncTime(); m_fLastLevelLoadTime = (t - m_levelLoadStartTime).GetSeconds(); LogLoadingTime(); m_nLoadedLevelsCount++; // Hide console after loading. gEnv->pConsole->ShowConsole(false); // gEnv->pCryPak->GetFileReadSequencer()->EndSection(); if (gEnv->pCharacterManager) { SAnimMemoryTracker amt; gEnv->pCharacterManager->SetAnimMemoryTracker(amt); } for (std::vector::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) { (*it)->OnLoadingComplete(pLevelInfo); } #if AZ_LOADSCREENCOMPONENT_ENABLED EBUS_EVENT(LoadScreenBus, Stop); #endif // if AZ_LOADSCREENCOMPONENT_ENABLED SEntityEvent loadingCompleteEvent(ENTITY_EVENT_LEVEL_LOADED); if (gEnv->pEntitySystem) { gEnv->pEntitySystem->SendEventToAll(loadingCompleteEvent); } } //------------------------------------------------------------------------ void CLevelSystem::OnLoadingProgress(ILevelInfo* pLevel, int progressAmount) { for (std::vector::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) { (*it)->OnLoadingProgress(pLevel, progressAmount); } } //------------------------------------------------------------------------ void CLevelSystem::OnUnloadComplete(ILevel* pLevel) { for (std::vector::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) { (*it)->OnUnloadComplete(pLevel); } } //------------------------------------------------------------------------ string& CLevelSystem::UnifyName(string& name) { //name.MakeLower(); name.replace('\\', '/'); return name; } ////////////////////////////////////////////////////////////////////////// void CLevelSystem::LogLoadingTime() { if (gEnv->IsEditor()) { return; } if (!GetISystem()->IsDevMode()) { return; } char vers[128]; GetISystem()->GetFileVersion().ToString(vers, sizeof(vers)); const char* sChain = ""; if (m_nLoadedLevelsCount > 0) { sChain = " (Chained)"; } string text; text.Format("Game Level Load Time: [%s] Level %s loaded in %.2f seconds%s", vers, m_lastLevelName.c_str(), m_fLastLevelLoadTime, sChain); gEnv->pLog->Log(text.c_str()); } void CLevelSystem::GetMemoryUsage(ICrySizer* pSizer) const { pSizer->AddObject(this, sizeof(*this)); pSizer->AddObject(m_levelInfos); pSizer->AddObject(m_levelsFolder); pSizer->AddObject(m_listeners); } void CLevelInfo::GetMemoryUsage(ICrySizer* pSizer) const { pSizer->AddObject(m_levelName); pSizer->AddObject(m_levelPath); pSizer->AddObject(m_levelPaks); //pSizer->AddObject(m_musicLibs); pSizer->AddObject(m_gamerules); pSizer->AddObject(m_gameTypes); } ////////////////////////////////////////////////////////////////////////// bool CLevelInfo::OpenLevelPak() { LOADING_TIME_PROFILE_SECTION; string levelpak = m_levelPath + string("/level.pak"); CryFixedStringT fullLevelPakPath; bool bOk = gEnv->pCryPak->OpenPack(levelpak, (unsigned)0, NULL, &fullLevelPakPath); m_levelPakFullPath.assign(fullLevelPakPath.c_str()); if (bOk) { string levelmmpak = m_levelPath + string("/levelmm.pak"); if (gEnv->pCryPak->IsFileExist(levelmmpak)) { gEnv->pCryPak->OpenPack(levelmmpak, (unsigned)0, NULL, &fullLevelPakPath); m_levelMMPakFullPath.assign(fullLevelPakPath.c_str()); } } gEnv->pCryPak->SetPacksAccessibleForLevel(GetName()); return bOk; } ////////////////////////////////////////////////////////////////////////// void CLevelInfo::CloseLevelPak() { LOADING_TIME_PROFILE_SECTION; if (!m_levelPakFullPath.empty()) { gEnv->pCryPak->ClosePack(m_levelPakFullPath.c_str(), ICryPak::FLAGS_PATH_REAL); stl::free_container(m_levelPakFullPath); } if (!m_levelMMPakFullPath.empty()) { gEnv->pCryPak->ClosePack(m_levelMMPakFullPath.c_str(), ICryPak::FLAGS_PATH_REAL); stl::free_container(m_levelMMPakFullPath); } } const bool CLevelInfo::IsOfType(const char* sType) const { for (unsigned int i = 0; i < m_levelTypeList.size(); ++i) { if (strcmp(m_levelTypeList[i], sType) == 0) { return true; } } return false; } ////////////////////////////////////////////////////////////////////////// void CLevelSystem::SaveOpenedFilesList() { if (!m_pLoadingLevelInfo) { return; } // Write resource list to file. string filename = PathUtil::Make(m_pLoadingLevelInfo->GetPath(), "resourcelist.txt"); AZ::IO::HandleType fileHandle = fxopen(filename.c_str(), "wt", true); if (fileHandle != AZ::IO::InvalidHandle) { IResourceList* pResList = gEnv->pCryPak->GetResourceList(ICryPak::RFOM_Level); for (const char* fname = pResList->GetFirst(); fname; fname = pResList->GetNext()) { AZ::IO::Print(fileHandle, "%s\n", fname); } gEnv->pFileIO->Close(fileHandle); } } ////////////////////////////////////////////////////////////////////////// void CLevelSystem::UnLoadLevel() { if (gEnv->IsEditor()) { return; } if (!m_pLoadingLevelInfo) { return; } CryLog("UnLoadLevel Start"); INDENT_LOG_DURING_SCOPE(); // Flush core buses. We're about to unload Cry modules and need to ensure we don't have module-owned functions left behind. AZ::Data::AssetBus::ExecuteQueuedEvents(); AZ::TickBus::ExecuteQueuedEvents(); AZ::MainThreadRenderRequestBus::ExecuteQueuedEvents(); if (gEnv && gEnv->pSystem) { // clear all error messages to prevent stalling due to runtime file access check during chainloading gEnv->pSystem->ClearErrorMessages(); } if (gEnv && gEnv->pCryPak) { gEnv->pCryPak->DisableRuntimeFileAccess(false); } CTimeValue tBegin = gEnv->pTimer->GetAsyncTime(); I3DEngine* p3DEngine = gEnv->p3DEngine; if (p3DEngine) { IDeferredPhysicsEventManager* pPhysEventManager = p3DEngine->GetDeferredPhysicsEventManager(); if (pPhysEventManager) { // clear deferred physics queues before renderer, since we could have jobs running // which access a rendermesh pPhysEventManager->ClearDeferredEvents(); } } //AM: Flush render thread (Flush is not exposed - using EndFrame()) //We are about to delete resources that could be in use if (gEnv->pRenderer) { gEnv->pRenderer->EndFrame(); // force a black screen as last render command gEnv->pRenderer->BeginFrame(); gEnv->pRenderer->SetState(GS_BLSRC_SRCALPHA | GS_BLDST_ONEMINUSSRCALPHA | GS_NODEPTHTEST); gEnv->pRenderer->Draw2dImage(0, 0, 800, 600, -1, 0.0f, 0.0f, 1.0f, 1.0f, 0.f, 0.0f, 0.0f, 0.0f, 1.0, 0.f); gEnv->pRenderer->EndFrame(); //flush any outstanding texture requests gEnv->pRenderer->FlushPendingTextureTasks(); } if (gEnv->pScriptSystem) { gEnv->pScriptSystem->ResetTimers(); } // Clear level entities and prefab instances. EBUS_EVENT(AzFramework::GameEntityContextRequestBus, ResetGameContext); if (gEnv->pEntitySystem) { gEnv->pEntitySystem->Unload(); // This needs to be called for editor and game, otherwise entities won't be released } // reset a bunch of subsystems if (gEnv->pDialogSystem) { gEnv->pDialogSystem->Reset(true); } if (gEnv->pAISystem) { gEnv->pAISystem->FlushSystem(true); gEnv->pAISystem->Reset(IAISystem::RESET_EXIT_GAME); gEnv->pAISystem->Reset(IAISystem::RESET_UNLOAD_LEVEL); } if (gEnv->pMovieSystem) { gEnv->pMovieSystem->Reset(false, false); gEnv->pMovieSystem->RemoveAllSequences(); } // Unload level specific audio binary data. Audio::SAudioManagerRequestData oAMData(Audio::eADS_LEVEL_SPECIFIC); Audio::SAudioRequest oAudioRequestData; oAudioRequestData.nFlags = (Audio::eARF_PRIORITY_HIGH | Audio::eARF_EXECUTE_BLOCKING); oAudioRequestData.pData = &oAMData; Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequestBlocking, oAudioRequestData); // Now unload level specific audio config data. Audio::SAudioManagerRequestData oAMData2(Audio::eADS_LEVEL_SPECIFIC); oAudioRequestData.pData = &oAMData2; Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequestBlocking, oAudioRequestData); Audio::SAudioManagerRequestData oAMData3(Audio::eADS_LEVEL_SPECIFIC); oAudioRequestData.pData = &oAMData3; Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequestBlocking, oAudioRequestData); if (gEnv->pEntitySystem) { gEnv->pEntitySystem->PurgeHeaps(); // This needs to be called for editor and game, otherwise entities won't be released } // Reset the camera to (0,0,0) which is the invalid/uninitialised state CCamera defaultCam; m_pSystem->SetViewCamera(defaultCam); OnUnloadComplete(m_pCurrentLevel); // -- kenzo: this will close all pack files for this level // (even the ones which were not added through here, if this is not desired, // then change code to close only level.pak) if (m_pLoadingLevelInfo) { ((CLevelInfo*)m_pLoadingLevelInfo)->CloseLevelPak(); m_pLoadingLevelInfo = NULL; } stl::free_container(m_lastLevelName); GetISystem()->GetIResourceManager()->UnloadLevel(); SAFE_RELEASE(m_pCurrentLevel); // Delete engine resources if (p3DEngine) { p3DEngine->UnloadLevel(); } // Force to clean render resources left after deleting all objects and materials. IRenderer* pRenderer = gEnv->pRenderer; if (pRenderer) { pRenderer->FlushRTCommands(true, true, true); CryComment("Deleting Render meshes, render resources and flush texture streaming"); // This may also release some of the materials. int flags = FRR_DELETED_MESHES | FRR_FLUSH_TEXTURESTREAMING | FRR_OBJECTS | FRR_RENDERELEMENTS | FRR_RP_BUFFERS | FRR_POST_EFFECTS; // Always keep the system resources around in the editor. // If a level load fails for any reason, then do not unload the system resources, otherwise we will not have any system resources to continue rendering the console and debug output text. if (!gEnv->IsEditor() && !GetLevelLoadFailed()) { flags |= FRR_SYSTEM_RESOURCES; } pRenderer->FreeResources(flags); CryComment("done"); } // Perform level unload procedures for the LyShine UI system if (gEnv && gEnv->pLyShine) { gEnv->pLyShine->OnLevelUnload(); } m_bLevelLoaded = false; CTimeValue tUnloadTime = gEnv->pTimer->GetAsyncTime() - tBegin; CryLog("UnLoadLevel End: %.1f sec", tUnloadTime.GetSeconds()); // Must be sent last. // Cleanup all containers GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_POST_UNLOAD, 0, 0); AzFramework::InputChannelRequestBus::Broadcast(&AzFramework::InputChannelRequests::ResetState); } DynArray* CLevelSystem::GetLevelTypeList() { return &m_levelTypeList; } } // namespace LegacyLevelSystem