diff options
Diffstat (limited to 'Runtime/Misc/SaveAndLoadHelper.cpp')
-rw-r--r-- | Runtime/Misc/SaveAndLoadHelper.cpp | 1106 |
1 files changed, 1106 insertions, 0 deletions
diff --git a/Runtime/Misc/SaveAndLoadHelper.cpp b/Runtime/Misc/SaveAndLoadHelper.cpp new file mode 100644 index 0000000..7bc0c2d --- /dev/null +++ b/Runtime/Misc/SaveAndLoadHelper.cpp @@ -0,0 +1,1106 @@ +#include "UnityPrefix.h" +#include "Configuration/UnityConfigure.h" +#include "SaveAndLoadHelper.h" +#include "RegisterAllClasses.h" + +#include "Runtime/Utilities/FileUtilities.h" +#include "Runtime/BaseClasses/GameObject.h" +#include "Runtime/BaseClasses/ManagerContextLoading.h" +#include "Configuration/UnityConfigureRevision.h" +#include "Runtime/Misc/BuildSettings.h" +#if ENABLE_UNITYGUI +#include "Runtime/IMGUI/GUIManager.h" +#endif +#include "Runtime/Terrain/TerrainData.h" +#include "Runtime/Misc/PreloadManager.h" +#include "Runtime/Input/TimeManager.h" +#include "Runtime/Utilities/GUID.h" +#include "Runtime/Animation/Animation.h" +#include "Runtime/Utilities/vector_set.h" +#include "Runtime/Input/InputManager.h" +#include "Runtime/Serialize/PersistentManager.h" +#include "Runtime/BaseClasses/IsPlaying.h" +#include "Runtime/Animation/AnimationManager.h" +#include "QualitySettings.h" +#include "Runtime/Animation/AnimationClip.h" +#if ENABLE_AUDIO +#include "Runtime/Audio/AudioClip.h" +#endif +#include "Runtime/Serialize/SerializedFile.h" +#include "Runtime/Profiler/ProfilerHistory.h" +#include "Runtime/Profiler/Profiler.h" +#include "Runtime/Profiler/MemoryProfilerStats.h" +#include "Runtime/Profiler/ProfilerConnection.h" +#include "Runtime/Network/PlayerCommunicator/PlayerConnection.h" +#include "CaptureScreenshot.h" +#include "Runtime/Graphics/Transform.h" +#include "GameObjectUtility.h" +#include "Runtime/Animation/Animation.h" +#include "Runtime/BaseClasses/Cursor.h" +#include "BatchDeleteObjects.h" + +#include "Runtime/Testing/Testing.h" +#include "Runtime/Camera/UnityScene.h" +#include "Runtime/Camera/RenderManager.h" +#include "Runtime/BaseClasses/Tags.h" +#include "Runtime/Serialize/TransferFunctions/RemapPPtrTransfer.h" +#include "Runtime/BaseClasses/IsPlaying.h" +#include "Runtime/Shaders/Material.h" +#include "Runtime/BaseClasses/ManagerContext.h" +#include "External/shaderlab/Library/shaderlab.h" +#if ENABLE_MONO +#include "Runtime/Mono/MonoIncludes.h" +#endif +#include "Runtime/GfxDevice/GfxDeviceSetup.h" +#include "Runtime/Misc/AssetBundle.h" +#include "Runtime/Misc/ResourceManager.h" +#include "Runtime/Input/GetInput.h" +#include "Runtime/Mono/MonoManager.h" +#include "Runtime/Mono/MonoAttributeHelpers.h" +#include "Runtime/Shaders/Shader.h" +#include "Runtime/Graphics/RenderBufferManager.h" +#include "Runtime/GfxDevice/GfxDevice.h" +#include "Runtime/Mono/MonoBehaviour.h" +#include "Runtime/GameCode/CallDelayed.h" +#include "Runtime/Filters/Renderer.h" +#include "Runtime/Graphics/ParticleSystem/ParticleSystem.h" // ParticleSystem::UpdateAll () +#include "Runtime/Filters/Particles/ParticleEmitter.h" +#include "Runtime/IMGUI/TextMeshGenerator2.h" +#include "Runtime/IMGUI/GUIClip.h" +#include "Runtime/Threads/JobScheduler.h" +#include "External/shaderlab/Library/pass.h" +#include "Runtime/Graphics/LightmapSettings.h" +#include "Runtime/Graphics/ScreenManager.h" +#include "Runtime/Misc/SystemInfo.h" +#include "Runtime/Utilities/Word.h" +#include "Runtime/Core/Callbacks/GlobalCallbacks.h" + +#include "Runtime/GameCode/CallDelayed.h" +#include "Runtime/Mono/MonoScript.h" +#include "Runtime/Network/PlayerCommunicator/PlayerConnection.h" +#include "Runtime/Serialize/AwakeFromLoadQueue.h" +#include "Runtime/Math/FloatExceptions.h" +#include "Runtime/Audio/AudioManager.h" + +#if UNITY_EDITOR +#include "Runtime/BaseClasses/CleanupManager.h" +#include "Editor/Src/HierarchyState.h" +#include "Editor/Src/EditorSettings.h" +#include "Editor/Src/Application.h" +#include "Editor/Src/AssetServer/ASCache.h" +#include "Editor/Src/EditorBuildSettings.h" +#include "Editor/Src/EditorUserBuildSettings.h" +#include "Editor/Src/SceneInspector.h" +#include "Editor/Src/Prefabs/Prefab.h" +#include "Editor/Src/AssetPipeline/AssetDatabase.h" +#include "Editor/Src/AssetPipeline/AssetInterface.h" + #if ENABLE_SPRITES + #include "Editor/Src/SpritePacker/SpritePacker.h" + #endif +#include "Editor/Src/BuildPipeline/LODGroupStripping.h" +#include "Editor/Src/GUIDPersistentManager.h" +#include "Editor/Src/EditorAssetGarbageCollectManager.h" +#include "Editor/Src/Prefabs/GenerateCachedTypeTree.h" +#include "Editor/Src/AssetPipeline/MonoCompilationPipeline.h" +#include "Player.h" +#include "PlayerSettings.h" +#endif + +#if ENABLE_WWW || ENABLE_CACHING +#include "PlatformDependent/CommonWebPlugin/UnityWebStream.h" +#include "CachingManager.h" +#include "PlatformDependent/CommonWebPlugin/CompressedFileStreamMemory.h" +#endif + + +#if SUPPORT_REPRODUCE_LOG +#include "ReproductionLog.h" +#endif + +#include "Runtime/Network/PlayerCommunicator/EditorConnection.h" + +const char* kMainData = "mainData"; + +using namespace std; + +std::string SelectDataFolder (); + +static void EditorBeforeLoadingCleanup (); +static void ResetManagersAfterLoadEditor (); +static void CollectAllSceneObjects (InstanceIDArray& instanceIDs); +static void CompleteAwakeSequence (const std::string& path, AwakeFromLoadQueue& awakeQueue); + +void DestroyWorld (bool destroySceneAssets) +{ + InstanceIDArray objects; + + #if UNITY_EDITOR + if (destroySceneAssets) + CollectAllSceneObjects (objects); + else + CollectSceneGameObjects (objects); + #else + Assert(!destroySceneAssets); + CollectSceneGameObjects (objects); + #endif + + Object* o; + // GameObjects first + for (InstanceIDArray::iterator i=objects.begin ();i != objects.end ();++i) + { + o = Object::IDToPointer (*i); + AssertIf (o && o->IsPersistent ()); + GameObject* go = dynamic_pptr_cast<GameObject*> (o); + // Only Destroy root level GameObjects. The children will be destroyed + // as part of them. That way, we ensure that the hierarchy is walked correctly, + // and all objects in the hieararchy will be marked as deactivated when destruction happens. + if (go != NULL && go->GetComponent(Transform).GetParent() == NULL) + DestroyObjectHighLevel (o); + } + + // normal objects whatever they might be after that + for (InstanceIDArray::iterator i=objects.begin ();i != objects.end ();++i) + { + o = Object::IDToPointer (*i); + AssertIf (o && o->IsPersistent ()); + DestroyObjectHighLevel (o); + } + + objects.clear (); + CollectLevelGameManagers (objects); + + // Gamemanagers & Scene last + for (InstanceIDArray::iterator i=objects.begin ();i != objects.end ();++i) + { + o = Object::IDToPointer (*i); + AssertIf (o && o->IsPersistent ()); + DestroyObjectHighLevel (o); + } + objects.clear (); + + GlobalCallbacks::Get().didUnloadScene.Invoke(); + + #if DEBUGMODE + ValidateNoSceneObjectsAreLoaded (destroySceneAssets); + #endif +} + +#if WEBPLUG +#define GET_DATA_FOLDER "" +#else +#define GET_DATA_FOLDER SelectDataFolder() +#endif + +bool InitializeEngineNoGraphics () +{ + static bool isInitialized = false; + if (isInitialized == false) + { +#if UNITY_EDITOR + EditorAssetGarbageCollectManager::StaticInitialize(); +#endif + #if SUPPORT_THREADS + Thread::mainThreadId = Thread::GetCurrentThreadID (); + #endif + #if ENABLE_PLAYERCONNECTION + #if UNITY_EDITOR + EditorConnection::Initialize(); + #else + PlayerConnection::Initialize(GET_DATA_FOLDER); + InstallPlayerConnectionLogging(true); + #endif + #endif + + #if ENABLE_PROFILER + InitializeMemoryProfilerStats(); + UnityProfiler::Initialize(); + ProfilerConnection::Initialize(); + #if UNITY_EDITOR + ProfilerHistory::Initialize(); + #endif + #endif + +#if ENABLE_PLAYERCONNECTION +#if UNITY_EDITOR + EditorConnection::Get().PollWithCustomMessage(); +#else + // Bug: Calls GetBuildSettings() inside, but it's not yet initialized !!! At least on windows standalone player +#if !UNITY_WIN && !UNITY_OSX && !UNITY_IPHONE + PlayerConnection::Get().Poll(); +#endif +#endif +#endif + + InitializeBatchDelete(); + RegisterAllClasses (); + Object::InitializeAllClasses (); + GameObject::InitializeMessageIdentifiers (); + ManagerContextInitializeClasses (); + RenderBufferManager::InitRenderBufferManager (); + +// On Linux Editor we initialize ScreenManager much earlier. Avoid doing that +// again here so we won't zero out the members. +#if !(UNITY_EDITOR && UNITY_LINUX) + InitScreenManager (); +#endif + + InitFloatExceptions(); + #if ENABLE_UNITYGUI + InitGUIManager (); + #endif + + // Init platform specific input support. + // Aras: on windows input must be initialized later, after the + // actual window is set up. + #if !UNITY_WIN && !UNITY_WII && !UNITY_IPHONE && !UNITY_ANDROID && !UNITY_FLASH && !UNITY_WEBGL && !UNITY_BB10 && !UNITY_TIZEN + InputInit(); + #endif + Object::CallInitializeClass (); + } + return true; +} + +#if (UNITY_OSX || (UNITY_LINUX && SUPPORT_X11)) && !UNITY_PEPPER +void SetGraphicsBatchMode( bool batch ); +#endif + +bool InitializeEngineGraphics (bool batch) +{ + static bool isInitialized = false; + if (isInitialized == false) + { + printf_console( "Initialize engine version: %s\n", UNITY_VERSION_FULL_NICE ); + +#if (UNITY_OSX || (UNITY_LINUX && SUPPORT_X11)) && !UNITY_PEPPER + SetGraphicsBatchMode( batch ); +#endif +#if UNITY_OSX || UNITY_FLASH || (UNITY_LINUX && WEBPLUG) || UNITY_WEBGL || (UNITY_WIN && !UNITY_WINRT) && !UNITY_PEPPER + if( !InitializeGfxDevice() ) + return false; +#endif + +#if ENABLE_PERFORMANCE_TESTS + RUN_PERFORMANCE_TESTS(); +#endif + + #if ENABLE_MULTITHREADED_CODE || ENABLE_MULTITHREADED_SKINNING + CreateJobScheduler(); + #endif + + ShaderLab::InitShaderLab (); + + Object::CallPostInitializeClass (); + GameObject::InitializeMessageHandlers (); + BuiltinResourceManager::InitializeAllResources (); + + // Make sure the default shaders are always preloaded to avoid loading them during + // threaded background loading + Shader::LoadDefaultShaders (); + + isInitialized = true; + GlobalCallbacks::Get().initializedEngineGraphics.Invoke(); + } + return true; +} + +void CleanupAllObjects (bool reloadable) +{ + vector<SInt32> objects; + + // Before each loop dealing with Objects here, we refetch all Objects. This is because + // deleting some game object might actually create new objects (i.e. components are + // dereferenced in GO destructor). + + // Delete all non-temporary game objects first + Object::FindAllDerivedObjects (ClassID (Object), &objects); + for (int i=0;i<objects.size();i++) + { + Object* o = Object::IDToPointer(objects[i]); + if (o && o->IsDerivedFrom(ClassID(GameObject)) && !o->IsPersistent() && !o->TestHideFlag(Object::kDontSave)) + { + DestroyObjectHighLevel (o); + } + } + + // Delete all game objects first + objects.clear(); + Object::FindAllDerivedObjects (ClassID (Object), &objects); + for (int i=0;i<objects.size();i++) + { + Object* o = Object::IDToPointer(objects[i]); +#if WEBPLUG + Transform* t = GetIdentityTransform(); +#endif + if (o && !o->IsPersistent() && o->IsDerivedFrom(ClassID(GameObject))) + { +#if WEBPLUG + if(reloadable && o == t->GetGameObjectPtr()) { + // THIS IS A MASSIVE HACK + // We do this because, when the Unity player is reloading, it frees all + // the gameobjects in memory. + // The Renderer object keeps a global static singleton Identity Transform, + // attached to an invisible game object that is created on first startup. + // Because the startup code is tied deeply into the initialization of + // the graphics engine, it's less hacky to Not Destroy that one game object. + // We seriously need to eliminate these global statics or place them under + // the management of a game manager object, which can refresh them as needed. + continue; + } +#endif + DestroyObjectHighLevel (o); + } + } + + LockObjectCreation(); + + // Do cleanup after all game objects are killed thus + // terrains will be deleted, and RenderTexture which are allocated by the render buffer manager will not be deleted yet. + TextMeshGenerator2::Flush(); + if (GetRenderBufferManagerPtr()) + GetRenderBufferManager().Cleanup(); + ShaderLab::Pass::DidClearAllTempRenderTextures (); + + // First of all we need to delete all non-temporary objects + // This is because usually there are dependencies from non-temporary objects + // to temporary objects + objects.clear(); + Object::FindAllDerivedObjects (ClassID (Object), &objects); + for (int i=0;i<objects.size();i++) + { + Object* o = Object::IDToPointer(objects[i]); + if (o && !o->IsDerivedFrom(ClassID(GameManager))) + { + if (o != NULL && o->TestHideFlag(Object::kDontSave)) + continue; + delete_object_internal (o); + } + } + + // Finally also cleanup all temporary objects + objects.clear(); + Object::FindAllDerivedObjects (ClassID (Object), &objects); + for (int i=0;i<objects.size();i++) + { + Object* o = Object::IDToPointer(objects[i]); + if (o && !o->IsDerivedFrom(ClassID(GameManager))) + { + if (reloadable) + { + if (o != NULL && o->TestHideFlag(Object::kDontSave)) + { + bool forceUnload = false; + // Make sure AssetBundles are unloaded. + if (o->GetClassID() == ClassID(AssetBundle)) + forceUnload = true; +#if ENABLE_SCRIPTING + if (o->GetClassID() == ClassID(MonoBehaviour)) + { + forceUnload = true; + if (strcmp(o->GetName(), "GameSkin") != 0) + AssertString(o->GetName()); + } + + if (o->GetClassID() == ClassID(MonoScript)) + { + forceUnload = true; + } +#endif + if (!forceUnload) + continue; + } + } + delete_object_internal (o); + } + } + + for (int i=ManagerContext::kManagerCount-1;i != 0;i--) + { + if (GetManagerContext().m_Managers[i]) + { + GetPersistentManager().MakeObjectUnpersistent(GetManagerContext().m_Managers[i]->GetInstanceID(), kDontDestroyFromFile); + delete_object_internal (GetManagerContext().m_Managers[i]); + SetManagerPtrInContext (i, NULL); + } + } + + objects.clear(); + Object::FindAllDerivedObjects (ClassID (Object), &objects); + for (int i=0;i<objects.size();i++) + { + Object* o = Object::IDToPointer(objects[i]); + if (reloadable) + { + // Don't cleanup temporary objects (Except if its a gui skin from the builtin resources file) + if (o != NULL && o->TestHideFlag(Object::kDontSave)) + continue; + } + + delete_object_internal (o); + } + + // Clear cached properties on all materials + // This is because reloading the web player might delete some referenced textures (gui style default materials) + // but not the material. The cache then gets out of sync and In that case, OpenGL will give an invalid error and text becomes black. + // Thus we force reloading the textures from scratch. + vector<Material*> materials; + Object::FindObjectsOfType (&materials); + for (int i=0;i<materials.size();i++) + { + materials[i]->ClearProperties(); + } + + #if CAPTURE_SCREENSHOT_AVAILABLE + FinishAllCaptureScreenshot (); + #endif + #if SUPPORT_REPRODUCE_LOG + PlayerCleanupReproduction(); + #endif + +#if ENABLE_PROFILER && UNITY_EDITOR + ProfilerHistory::Cleanup(); +#endif + + UnlockObjectCreation(); + + CleanupBatchDelete (); +} + +void CleanupEngine () +{ + if (IsGfxDevice()) + GetGfxDevice().FinishRendering(); + TextMeshGenerator2::Flush(); + CleanupAllObjects(false); + MessageIdentifier::Cleanup (); + Object::CleanupAllClasses (); + CleanupShaders (); + RenderBufferManager::CleanupRenderBufferManager (); + #if ENABLE_UNITYGUI + CleanupGUIManager (); + #endif + #if ENABLE_MULTITHREADED_CODE || ENABLE_MULTITHREADED_SKINNING + DestroyJobScheduler(); + #endif + // on windows, device cleanup is done from separately (web player: called from separate thread) + #if UNITY_OSX + DestroyGfxDevice(); + #endif + + #if !UNITY_FLASH + Cursors::CleanupCursors (); + #endif + + ReleaseScreenManager(); + + #if ENABLE_PLAYERCONNECTION && !UNITY_EDITOR + InstallPlayerConnectionLogging(false); + #endif +#if UNITY_EDITOR + EditorAssetGarbageCollectManager::StaticDestroy(); + CleanupTypeTreeCache(); + CleanupMonoCompilationPipeline(); +#endif + ReleaseLogHandlers(); +#if ENABLE_PROFILER + CleanupMemoryProfilerStats(); +#endif +} + +void PostprocessSceneGenerateGUITextureAtlas () +{ +#if UNITY_EDITOR && ENABLE_RETAINEDGUI + std::vector<Canvas*> canvas; + Object::FindObjectsOfType(&canvas); + + for( unsigned i = 0 ; i < canvas.size() ; ++ i) + BuildTextureAtlasForCanvas(canvas[i]); +#endif +} + +#if ENABLE_EDITOR_HIERARCHY_ORDERING +void PostprocessSceneSortTransforms() +{ + const Transform::VisibleRootMap& rootMap = GetSceneTracker().GetVisibleRootTransforms(); + + for (Transform::VisibleRootMap::const_iterator it = rootMap.begin(); it != rootMap.end(); ++it) + { + Transform* rootTrans = (*it).second; + rootTrans->OrderChildrenRecursively(); + } +} +#endif + +void PostprocessScene () +{ +#if UNITY_EDITOR + // Disconnect all prefab instances. + // This ensures that no prefab recording will happen which can be quite expensive. + DisconnectAllPrefabInstances (); + + DestroyRenderersFromLODGroupInOpenScene(GetQualitySettings().GetStrippedMaximumLODLevel()); + ScriptingArguments no_arguments; + CallMethodsWithAttribute(MONO_COMMON.postProcessSceneAttribute, no_arguments, NULL); + PostprocessSceneGenerateGUITextureAtlas(); +#endif + +#if ENABLE_EDITOR_HIERARCHY_ORDERING + PostprocessSceneSortTransforms(); +#endif +} + +void CleanupAfterLoad () +{ + #if UNITY_EDITOR + GetCleanupManager ().Flush (); + RemoveDuplicateGameManagers (); + #endif + + GarbageCollectSharedAssets (true); + + TextMeshGenerator2::Flush(); + GetRenderBufferManager().GarbageCollect(0); + GetGfxDevice().InvalidateState(); + + #if ENABLE_MONO + mono_gc_collect (mono_gc_max_generation ()); + #endif + + ParticleSystem::BeginUpdateAll (); + ParticleSystem::EndUpdateAll (); + ParticleEmitter::UpdateAllParticleSystems(); + RenderManager::UpdateAllRenderers(); + + // FIXME: Do this because we can't fully initialize the physicsmanager at startup. + CALL_MANAGER_IF_EXISTS(ManagerContext::kPhysicsManager, AwakeFromLoad (kDefaultAwakeFromLoad)) + + GetDelayedCallManager().Update(DelayedCallManager::kAfterLoadingCompleted); + + + ///@TODO: I am not sure why we do this... + GetQualitySettings().ApplySettings(); +} + +void PatchRendererLightmapIndices (AwakeFromLoadQueue& awakeQueue) +{ + // offset the lightmap index in the newly loaded Renderers and Terrains by the current number of lightmaps + int lightmapIndexOffset = GetLightmapSettings().GetLightmaps().size(); + if (lightmapIndexOffset == 0) + return; + + AwakeFromLoadQueue::ItemArray& rendererItems = awakeQueue.GetItemArray(kGameObjectAndComponentQueue); + for (int i = 0; i < rendererItems.size(); i++) + { + Object* object = Object::IDToPointer(rendererItems[i].objectPPtr.GetInstanceID()); + + // renderers + Renderer* r = dynamic_pptr_cast<Renderer*>(object); + if (r != NULL) + { + if (r->IsLightmappedForRendering()) + r->SetLightmapIndexInt(r->GetLightmapIndexInt() + lightmapIndexOffset); + } + } + + #if ENABLE_TERRAIN + PPtr<MonoScript> terrainScript = GetBuiltinResource<MonoScript>("Terrain"); + AwakeFromLoadQueue::ItemArray& monoBehaviourItems = awakeQueue.GetItemArray(kMonoBehaviourQueue); + for (int i = 0; i < monoBehaviourItems.size(); i++) + { + Object* object = Object::IDToPointer(monoBehaviourItems[i].objectPPtr.GetInstanceID()); + + // terrains + MonoBehaviour* behaviour = dynamic_pptr_cast<MonoBehaviour*>(object); + if (behaviour && behaviour->GetScript() == terrainScript) + { + MessageData data; + data.SetData (lightmapIndexOffset, ClassID (int)); + SendMessageDirect(*behaviour, kShiftLightmapIndex, data); + } + } + #endif + +} + +void MergeLightmapData(AwakeFromLoadQueue& awakeQueue) +{ + // We made sure the LightmapSettings object will get loaded along with the other stuff + // in PreloadLevelOperation::Perform(). Now let's update the lightmap indices of the + // loaded Renderers, append loaded lightmaps to the original lightmaps and destroy + // the loaded LightmapSettings object. + LightmapSettings* loadedLightmapSettings = NULL; + + AwakeFromLoadQueue::ItemArray& managerItems = awakeQueue.GetItemArray(kManagersQueue); + for (int i = 0; i < managerItems.size(); i++) + { + loadedLightmapSettings = dynamic_instanceID_cast<LightmapSettings*> (managerItems[i].objectPPtr.GetInstanceID()); + if (loadedLightmapSettings) + break; + } + if (loadedLightmapSettings == NULL) + return; + + const std::vector<LightmapData>& loadedLightmapData = loadedLightmapSettings->GetLightmaps(); + + // loaded level contains lightmaps, so merge them. + if (loadedLightmapData.size() != 0) + { + int lightmapsMode = GetLightmapSettings ().GetLightmapsMode (); + int loadedLightmapsMode = loadedLightmapSettings->GetLightmapsMode (); + + if (loadedLightmapsMode != lightmapsMode) + { + WarningString (Format("The loaded level has a different lightmaps mode than the current one. Current: %s. Loaded: %s. Will use: %s.", + LightmapSettings::kLightmapsModeNames[lightmapsMode], + LightmapSettings::kLightmapsModeNames[loadedLightmapsMode], + LightmapSettings::kLightmapsModeNames[lightmapsMode])); + } + + // first offset lightmap indices of the loaded renderers by the current number of lightmaps + PatchRendererLightmapIndices(awakeQueue); + // then add the new lightmaps at the end of the current lightmaps array + GetLightmapSettings().AppendLightmaps(loadedLightmapData); + } + + DestroyObjectHighLevel(loadedLightmapSettings); +} + +void PostLoadLevelAdditive (const std::string& pathName, AwakeFromLoadQueue& awakeQueue) +{ + PostEditorLoadLevelAdditive(pathName, awakeQueue); + + PostprocessScene(); +} + +void PostEditorLoadLevelAdditive (const std::string& pathName, AwakeFromLoadQueue& awakeQueue) +{ + awakeQueue.RegisterObjectInstanceIDs(); + + CompleteAwakeSequence(pathName, awakeQueue); + + MergeLightmapData(awakeQueue); +} + +void VerifyNothingIsPersistentInLoadedScene (const std::string& pathName) +{ +#if DEBUGMODE + set<SInt32> persistentObjectsAtPath; + GetPersistentManager().GetPersistentInstanceIDsAtPath(pathName, &persistentObjectsAtPath); + for (set<SInt32>::iterator i=persistentObjectsAtPath.begin();i!=persistentObjectsAtPath.end();i++) + { + Object* target = Object::IDToPointer(*i); + // Error on everything but global game managers. this handles the case where mainData contains both scene data and global game managers. + if (target == NULL || !Object::IsDerivedFromClassID(target->GetClassID(), ClassID(GlobalGameManager)) || UNITY_EDITOR) + { + string className = target ? target->GetClassName() : "Not loaded"; + ErrorString("Failed to unpersist: " + className + " ID: " + IntToString(*i) + " FileID: " + IntToString(GetPersistentManager().GetLocalFileID(*i))); + } + } + AssertIf(GetPersistentManager().IsStreamLoaded(pathName) && !GetPersistentManager().HasMemoryOrCachedSerializedFile(pathName)); +#endif +} + +void ValidateNoSceneObjectsAreLoaded (bool includeAllEditorExtensions) +{ +#if (UNITY_EDITOR && DEBUGMODE) || !UNITY_RELEASE + // This happens when objects are accidentally loaded from disk again because someone still had a pointer to them. + InstanceIDArray objects; +#if UNITY_EDITOR + if (includeAllEditorExtensions) + CollectAllSceneObjects (objects); + else +#endif + CollectSceneGameObjects (objects); + if (!objects.empty ()) + { + ErrorString ("Some objects were not cleaned up when closing the scene. (Did you spawn new GameObjects from OnDestroy?)"); + } + + vector<Object*> levelManagers; + Object::FindObjectsOfType (ClassID (LevelGameManager), &levelManagers); + for (int i=0;i<levelManagers.size();i++) + { + ErrorString (Format("Manager %s is still loading after clearing scene", levelManagers[i]->GetClassName().c_str())); + } +#endif +} + +void CompletePreloadMainData (AwakeFromLoadQueue& awakeQueue) +{ + ResetInput(); + + // Cleanup exisiting level maangers + DestroyLevelManagers (); + + awakeQueue.RegisterObjectInstanceIDs(); + + // Load LevelManagers + LoadManagers(awakeQueue); + + // Load everything else + CompleteAwakeSequence(kMainData, awakeQueue); + + // FIXME: Do this because we can't fully initialize the physicsmanager at startup. + CALL_MANAGER_IF_EXISTS(ManagerContext::kPhysicsManager, AwakeFromLoad (kDefaultAwakeFromLoad)) + + GetDelayedCallManager().Update(DelayedCallManager::kAfterLoadingCompleted); + + GetQualitySettings().ApplySettings(); +} + +void CompletePreloadManagerLoadLevel (const std::string& path, AwakeFromLoadQueue& awakeQueue) +{ + ResetInput(); + + awakeQueue.RegisterObjectInstanceIDs(); + + LoadManagers(awakeQueue); + + CompleteAwakeSequence(path, awakeQueue); + + PostprocessScene(); + + CleanupAfterLoad (); +} + +static void CompleteAwakeSequence (const std::string& path, AwakeFromLoadQueue& awakeQueue) +{ +#if UNITY_EDITOR + // Merge all prefab instances (Requires that prefab backwards compatilibyt has been applied - AwakeFromLoad has not yet been called) + MergeAllPrefabInstances(&awakeQueue); +#endif + + // Unload stream + // - Don't unload if the stream came from an AssetBundle + if ( !GetPersistentManager().HasMemoryOrCachedSerializedFile( path ) ) + GetPersistentManager().UnloadStream(path); + + // Invoke AwakeFromLoad and friends. + GetPersistentManager().IntegrateAllThreadedObjectsStep2(awakeQueue); + + + //@TODO: write a check that all objects in the AwakeQueue are !IsPersistent() + +#if UNITY_EDITOR + GetSceneTracker().ReloadTransformHierarchyRoots(); +#endif + +} + +#if UNITY_EDITOR + +void LoadLevelAdditiveEditor (const std::string& level) +{ + PreloadLevelOperation* op = PreloadLevelOperation::LoadLevel (level, "", -1, PreloadLevelOperation::kLoadEditorAdditiveLevel, true); + + GetPreloadManager().WaitForAllAsyncOperationsToComplete(); + + op->Release(); +} + +void CompletePreloadManagerLoadLevelEditor (const std::string& path, AwakeFromLoadQueue& awakeQueue, int preloadOperationMode) +{ + Assert (!path.empty ()); + + awakeQueue.RegisterObjectInstanceIDs(); + + LoadManagers(awakeQueue); + + CompleteAwakeSequence(path, awakeQueue); + + if (preloadOperationMode == PreloadLevelOperation::kOpenSceneEditorPlaymode) + PostprocessScene (); + + CleanupAfterLoad (); + + ResetManagersAfterLoadEditor (); + + if (preloadOperationMode == PreloadLevelOperation::kOpenSceneEditorPlaymode) + PlayerInitState(); +} + +bool LoadSceneEditor (const string& pathName, std::map<LocalIdentifierInFileType, SInt32>* hintFileIDToHeapID, int options) +{ + AssertIf (pathName.empty ()); + + bool enterPlaymode = options & kEditorPlayMode; + PreloadLevelOperation::LoadingMode preloadOperationMode = enterPlaymode ? PreloadLevelOperation::kOpenSceneEditorPlaymode : PreloadLevelOperation::kOpenSceneEditor; + + // Make sure nothing is in the queue (We are calling DestroyWorld, then doing AsyncLoad) + GetPreloadManager().WaitForAllAsyncOperationsToComplete(); + + // Destroy world must be called before we start loading. + // Otherwise objects will receive different instanceIDs in Playmode then they have in edit mode (hintFileIDToHeapID) + DestroyWorld(true); + EditorBeforeLoadingCleanup (); + SetIsWorldPlaying (enterPlaymode); + + // Any left-over playmode load operations need to be cleared here. + GetPreloadManager().RemoveStopPlaymodeOperations (); + + // Now load + if (hintFileIDToHeapID) + GetPersistentManager().SuggestFileIDToHeapIDs (pathName, *hintFileIDToHeapID); + + PreloadLevelOperation* op = PreloadLevelOperation::LoadLevel (pathName, "", -1, preloadOperationMode, true); + + GetPreloadManager().WaitForAllAsyncOperationsToComplete(); + + op->Release(); + + return true; +} +#endif + + +static void DestroyAllAtPath (const std::string& path) +{ +#if DEBUGMODE + GetPersistentManager().SetDebugAssertLoadingFromFile(path); +#endif + PersistentManager::ObjectIDs ids; + GetPersistentManager().GetLoadedInstanceIDsAtPath(path, &ids); + + LockObjectCreation(); + for (PersistentManager::ObjectIDs::iterator i=ids.begin();i!=ids.end();i++) + { + Object* o = Object::IDToPointer (*i); + AssertIf(o != NULL && !o->IsPersistent()); + delete_object_internal (o); + } + UnlockObjectCreation(); + +#if DEBUGMODE + ids.clear(); + GetPersistentManager().GetLoadedInstanceIDsAtPath(path, &ids); + if (!ids.empty()) + { + ErrorString("UnloadAssetBundle failed"); + } + GetPersistentManager().SetDebugAssertLoadingFromFile(""); +#endif + + GetPersistentManager().RemoveObjectsFromPath(path); +} + +static bool DoesGfxDeviceRequireAssetsToBeReloadableFromDisk () +{ +// For Windows Phone 8 reloading may be required at any point in time (when user pauses - resumes app, for instance) +#if UNITY_WP8 + return true; +#else + return false; +#endif +} + +void UnloadAssetBundle (AssetBundle& file, bool unloadAllLoadedObjects) +{ + if (DoesGfxDeviceRequireAssetsToBeReloadableFromDisk () && !unloadAllLoadedObjects) + { + ErrorString("AssetBundle.Unload(false) shouldn't be called because used assets might have to be reloaded at any point in time. If no assets are used, call AssetBundle.Unload(true)."); + return; + } + + GetPreloadManager().LockPreloading(); + + PPtr<AssetBundle> resourceFilePPtr = &file; + + // Destroy all objects loaded from the AssetBundle path (or unassociate + // them from the path if unloadAllLoadedObjects==false) and unload + // all streams associated with the bundle. + // + // Note that DestroyAllAtPath() will actually delete the AssetBundle + // instance itself so be careful not to reference any data from it + // after the call. + + #if ENABLE_WWW + UnityWebStream* stream = file.m_UnityWebStream; + if (stream && stream->GetFileStream() && + (stream->GetFileStream()->GetType() == kCompressedFileStreamMemoryType || + stream->GetFileStream()->GetType() == kUncompressedFileStreamMemoryType)) + { + FileStream* compressedFile = stream->GetFileStream(); + FileStream::Decompressed files = compressedFile->m_Files; + + for (FileStream::iterator f=files.begin();f != files.end();f++) + { + if (unloadAllLoadedObjects) + DestroyAllAtPath(f->name); + else + GetPersistentManager().RemoveObjectsFromPath(f->name); + } + + DestroyWithoutLoadingButDontDestroyFromFile(resourceFilePPtr.GetInstanceID()); + + for (FileStream::iterator i=files.begin();i != files.end();i++) + { + GetPersistentManager().UnloadStream(i->name); + } + } + else + #endif // ENABLE_WWW + #if ENABLE_CACHING + if (file.m_CachedUnityWebStream) + { + // Don't const reference files, as the changed is being changed in the loop + vector<string> files = file.m_CachedUnityWebStream->m_Files; + + for (int i=0;i<files.size();i++) + { + const string& path = files[i]; + if (unloadAllLoadedObjects) + DestroyAllAtPath(path); + else + GetPersistentManager().RemoveObjectsFromPath(path); + } + + DestroyWithoutLoadingButDontDestroyFromFile(resourceFilePPtr.GetInstanceID()); + + for (int i=0;i<files.size();i++) + { + GetPersistentManager().UnloadStream(files[i]); + } + } + else + #endif // ENABLE_CACHING + if (file.m_UncompressedFileInfo) + { + AssetBundle::UncompressedFileInfoContainer* uncompFileInfo = file.m_UncompressedFileInfo; + + for (AssetBundle::UncompressedFileInfoContainer::iterator f = uncompFileInfo->begin(); + f != uncompFileInfo->end(); + f++) + { + const string& path = f->fileName; + if (unloadAllLoadedObjects) + DestroyAllAtPath(path); + else + GetPersistentManager().RemoveObjectsFromPath(path); + } + + DestroyWithoutLoadingButDontDestroyFromFile(resourceFilePPtr.GetInstanceID()); + + for (AssetBundle::UncompressedFileInfoContainer::iterator f = uncompFileInfo->begin(); + f != uncompFileInfo->end(); + f++) + { + GetPersistentManager().UnloadStream(f->fileName); + } + UNITY_DELETE(uncompFileInfo, kMemFile); + } + else + { + ErrorString("Resource file has already been unloaded."); + } + + GetPreloadManager().UnlockPreloading(); +} + + +#if UNITY_EDITOR + + +static void EditorBeforeLoadingCleanup () +{ + GetDelayedCallManager ().ClearAll (); +} + +static void ResetManagersAfterLoadEditor () +{ + ResetInput(); + GetTimeManager ().ResetTime (); + GetQualitySettings().ApplySettings(); +} + +void CreateWorldEditor () +{ + // Create any managers that are not created yet and setup manager context + AwakeFromLoadQueue emptyQueue (kMemTempAlloc); + LoadManagers(emptyQueue); + + EditorBeforeLoadingCleanup (); + + GetSceneTracker().ReloadTransformHierarchyRoots(); + + ResetManagersAfterLoadEditor (); +} +#endif + + +PROFILER_INFORMATION(gCollectSceneGameObjects, "CollectGameObjects", kProfilerLoading) + +void CollectSceneGameObjects (InstanceIDArray& outputObjects) +{ + PROFILER_AUTO(gCollectSceneGameObjects,NULL); + vector<GameObject*> gameObjects; + Object::FindObjectsOfType (&gameObjects); + for (vector<GameObject*>::iterator i= gameObjects.begin ();i != gameObjects.end ();++i) + { + GameObject& object = **i; + + // Verify that game objects dont accidentally end up being active and persistent + #if DEBUGMODE + if (object.IsActive() && object.IsPersistent()) + { + ErrorStringObject("Persistent object inconsistency", &object); + } + #endif + + if (object.IsPersistent ()) + continue; + if (object.TestHideFlag (Object::kDontSave)) + continue; + + #if UNITY_EDITOR + if (object.IsPrefabParent ()) + { + ErrorStringObject ("A prefab somehow lost its way out of its asset file. Ignoring it. Save your scene and next time you launch the editor it will be gone.", &object); + continue; + } + #endif + + outputObjects.push_back(object.GetInstanceID ()); + } +} +#if UNITY_EDITOR +static void CollectAllSceneObjects (InstanceIDArray& instanceIDs) +{ + vector<SInt32> objects; + set<Prefab*> prefabInstances; + Object::FindAllDerivedObjects (ClassID (EditorExtension), &objects); + for (vector<SInt32>::iterator i= objects.begin ();i != objects.end ();++i) + { + EditorExtension& object = *PPtr<EditorExtension> (*i); + +#if DEBUGMODE + GameObject* go = dynamic_pptr_cast<GameObject*> (&object); + if (go) + { + if (go->IsActive() && go->IsPersistent()) + { + ErrorStringObject("Persistent object inconsistency", go); + } + } +#endif + + if (object.IsPersistent ()) + continue; + + if (object.IsPrefabParent()) + { + ErrorStringObject ("A prefab somehow lost its way out of its asset file. Ignoring it. Save your scene and next time you launch the editor it will be gone.", &object); + continue; + } + + if (object.TestHideFlag (Object::kDontSave)) + continue; + if (object.IsDerivedFrom (ClassID (GameManager))) + continue; + if (object.GetClassID () >= ClassID (SmallestEditorClassID)) + continue; + + instanceIDs.push_back(object.GetInstanceID ()); + } +} + +#endif + |