Kaynağa Gözat

feat: 添加Pak挂载使用代码

Nicetry12138 1 yıl önce
ebeveyn
işleme
b4be46b150
1 değiştirilmiş dosya ile 845 ekleme ve 0 silme
  1. 845 0
      UE5/热更/README.md

+ 845 - 0
UE5/热更/README.md

@@ -1,3 +1,848 @@
+# UE的启动流程
+
+[动态加载 PakFile](https://www.cnblogs.com/psklf/p/14778289.html)
+
+在上述文章中简单介绍了一点 UE 的启动流程,大概在什么时候开始挂载 Pak 文件
+
+## LaunchWindowsStartup
+
+引擎从 `LaunchWindowsStartup` 函数开始,在解析完 `CmdLine` 并根据命令函参数进行属性设置之后,将 `CmdLine` 传入给 `GuardedMain` 函数
+
+```cpp
+// 在 LaunchWindowsStartup 根据 CmdLine 参数设置报错类型和全局变量
+
+// If we're running in unattended mode, make sure we never display error dialogs if we crash.
+if ( FParse::Param( CmdLine, TEXT("unattended") ) )
+{
+    SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);
+}
+
+if ( FParse::Param( CmdLine,TEXT("crashreports") ) )
+{
+    GAlwaysReportCrash = true;
+}
+```
+
+## GuardedMain
+
+在 `GuardedMain` 中
+
+1. 根据 `CmdLine` 是否存在 `WaitForDebugger` 或者 `waitforattach` 参数决定是否要等待附加进程
+
+```cpp
+#if !(UE_BUILD_SHIPPING)
+	// If "-waitforattach" or "-WaitForDebugger" was specified, halt startup and wait for a debugger to attach before continuing
+	if (FParse::Param(CmdLine, TEXT("waitforattach")) || FParse::Param(CmdLine, TEXT("WaitForDebugger")))
+	{
+		while (!FPlatformMisc::IsDebuggerPresent())
+		{
+			FPlatformProcess::Sleep(0.1f);
+		}
+		UE_DEBUG_BREAK();
+	}
+#endif
+```
+
+2. 广播 `GetPreMainInitDelegate` 事件,通知即将开始 `EnginePreInit`
+
+3. 创建 `EngineLoopCleanupGuard` 临时对象,`GuardedMain` 函数结束时对象被释放从而触发析构函数,进入出发 `EngineExit` 函数
+
+```cpp
+// make sure GEngineLoop::Exit() is always called.
+struct EngineLoopCleanupGuard 
+{ 
+    ~EngineLoopCleanupGuard()
+    {
+        // Don't shut down the engine on scope exit when we are running embedded
+        // because the outer application will take care of that.
+        if (!GUELibraryOverrideSettings.bIsEmbedded)
+        {
+            EngineExit();
+        }
+    }
+} CleanupGuard;
+```
+
+
+4. 触发 `EnginePreInit` 进入引擎预先初始化阶段
+
+5. 根据 `GIsEditor` 判断是否是编辑器,分别进入 `EditorInit` 或者 `EngineInit` 真正初始化函数
+
+```cpp
+// 在 PreInit 中通过宏来设定 GIsEditor 的值
+#if WITH_EDITORONLY_DATA
+		GIsEditor = true;
+#endif
+```
+
+6. 开始引擎的主循环
+
+```cpp
+if (!GUELibraryOverrideSettings.bIsEmbedded)
+{
+    while( !IsEngineExitRequested() )
+    {
+        EngineTick();
+    }
+}
+```
+
+7. 退出引擎时,根据 `GIsEditor` 来执行推出编辑器逻辑
+
+```cpp
+#if WITH_EDITOR
+	if( GIsEditor )
+	{
+		EditorExit();
+	}
+#endif
+```
+
+## PreInit
+
+```cpp
+int32 EnginePreInit( const TCHAR* CmdLine )
+{
+	int32 ErrorLevel = GEngineLoop.PreInit( CmdLine );
+
+	return( ErrorLevel );
+}
+```
+
+`EnginePreInit` 本质就是调用 `GEngineLoop.PreInit`
+
+```cpp
+int32 FEngineLoop::PreInit(const TCHAR* CmdLine)
+{
+	const int32 rv1 = PreInitPreStartupScreen(CmdLine);
+	if (rv1 != 0)
+	{
+		PreInitContext.Cleanup();
+		return rv1;
+	}
+
+	const int32 rv2 = PreInitPostStartupScreen(CmdLine);
+	if (rv2 != 0)
+	{
+		PreInitContext.Cleanup();
+		return rv2;
+	}
+
+	return 0;
+}
+```
+
+`PreInit` 分为两个粒度: `PreInitPreStartupScreen` 和 `PreInitPostStartupScreen`,也就是打开开始界面和打开开始界面后
+
+### PreInitPreStartupScreen
+
+1. 运行并清除延迟自动注册的委托
+
+```cpp
+FDelayedAutoRegisterHelper::RunAndClearDelayedAutoRegisterDelegates(EDelayedRegisterRunPhase::StartOfEnginePreInit);
+```
+
+2. 设置日志的主线程
+
+```cpp
+if (GLog)
+{
+    GLog->SetCurrentThreadAsPrimaryThread();
+}
+```
+
+3. 解析命令行参数
+4. 设置游戏标志
+
+```cpp
+#if UE_BUILD_DEVELOPMENT && defined(UE_BUILD_DEVELOPMENT_WITH_DEBUGGAME) && UE_BUILD_DEVELOPMENT_WITH_DEBUGGAME
+FApp::SetDebugGame(true);
+#endif
+```
+
+5. 注册平台特定的处理器
+
+```cpp
+#if PLATFORM_WINDOWS
+FWindowsPlatformMisc::SetGracefulTerminationHandler();
+#endif
+```
+
+6. 嵌入式应用程序的初始化
+
+```cpp
+#if BUILD_EMBEDDED_APP
+FEmbeddedCommunication::Init();
+FEmbeddedCommunication::KeepAwake(TEXT("Startup"), false);
+#endif
+```
+
+7. 设置线程本地存储 TLS 缓存
+
+```cpp
+FMemory::SetupTLSCachesOnCurrentThread();
+```
+
+8. 设置 UTF-8 输出
+
+```cpp
+if (FParse::Param(CmdLine, TEXT("UTF8Output")))
+{
+    FPlatformMisc::SetUTF8Output();
+}
+```
+
+9.  切换工作目录
+
+```cpp
+FPlatformProcess::SetCurrentWorkingDirectoryToBaseDir();
+```
+
+10. 从环境变量中获取额外的命令行参数
+
+```cpp
+#if !UE_BUILD_SHIPPING && WITH_EDITORONLY_DATA
+FString Env = FPlatformMisc::GetEnvironmentVariable(TEXT("UE-CmdLineArgs")).TrimStart();
+if (Env.Len())
+{
+    FCommandLine::Append(TEXT(" -EnvAfterHere "));
+    FCommandLine::Append(*Env);
+    CmdLine = FCommandLine::Get();
+}
+#endif
+```
+
+11. 设置游戏名称
+
+```cpp
+if (LaunchSetGameName(CmdLine, GameProjectFilePathUnnormalized) == false)
+{
+    return 1;
+}
+```
+
+12. 初始化跟踪系统,用于性能分析和调试
+
+```cpp
+FTraceAuxiliary::Initialize(CmdLine);
+FTraceAuxiliary::TryAutoConnect();
+```
+
+13. 根据命令行参数初始化低级内存管理系统,可能包括内存分析工具
+
+```cpp
+LLM(FLowLevelMemTracker::Get().ProcessCommandLine(CmdLine));
+#if MEMPRO_ENABLED
+FMemProProfiler::Init(CmdLine);
+#endif
+```
+
+14. 注册垃圾回收和采样输入的回调
+
+```cpp
+#if WITH_ENGINE
+FCoreUObjectDelegates::PostGarbageCollectConditionalBeginDestroy.AddStatic(DeferredPhysResourceCleanup);
+#endif
+FCoreDelegates::OnSamplingInput.AddStatic(UpdateGInputTime);
+```
+
+15. 初始化 IO 调度器
+
+```cpp
+#if USE_IO_DISPATCHER
+if (FIoStatus Status = FIoDispatcher::Initialize(); !Status.IsOk())
+{
+    return 1;
+}
+#endif
+```
+
+16. 平台相关初始化
+
+```cpp
+    // platform specific initialization now that the SystemSettings are loaded
+    FPlatformMisc::PlatformInit();
+#if WITH_APPLICATION_CORE
+    FPlatformApplicationMisc::Init();
+#endif
+    FPlatformMemory::Init();
+```
+
+17. 进入主线程、设置线程池
+
+```cpp
+if (bCreateTaskGraphAndThreadPools)
+{
+    // initialize task graph sub-system with potential multiple threads
+    SCOPED_BOOT_TIMING("FTaskGraphInterface::Startup");
+    FTaskGraphInterface::Startup(FPlatformMisc::NumberOfWorkerThreadsToSpawn());
+    FTaskGraphInterface::Get().AttachToThread(ENamedThreads::GameThread);
+}
+
+if (FPlatformProcess::SupportsMultithreading() && bCreateTaskGraphAndThreadPools)
+{
+    // do some thing
+}
+```
+
+18. 加载核心模块
+
+```cpp
+#if STATS
+	FThreadStats::StartThread();
+#endif
+
+	FDelayedAutoRegisterHelper::RunAndClearDelayedAutoRegisterDelegates(EDelayedRegisterRunPhase::StatSystemReady);
+
+	FScopeCycleCounter CycleCount_AfterStats(GET_STATID(STAT_FEngineLoop_PreInitPreStartupScreen_AfterStats));
+
+	// Load Core modules required for everything else to work (needs to be loaded before InitializeRenderingCVarsCaching)
+	{
+		SCOPED_BOOT_TIMING("LoadCoreModules");
+		if (!LoadCoreModules())
+		{
+			UE_LOG(LogInit, Error, TEXT("Failed to load Core modules."));
+			return 1;
+		}
+	}
+```
+
+19. 配置文件读取和热修复
+
+```cpp
+if (bDumpEarlyConfigReads)
+{
+    UE::ConfigUtilities::RecordConfigReadsFromIni();
+}
+
+if (bDumpEarlyPakFileReads)
+{
+    RecordFileReadsFromPaks();
+}
+
+if(bWithConfigPatching)
+{
+    UE_LOG(LogInit, Verbose, TEXT("Begin recording CVar changes for config patching."));
+
+    UE::ConfigUtilities::RecordApplyCVarSettingsFromIni();
+}
+
+UE::ConfigUtilities::ApplyCVarsFromBootHotfix();
+```
+
+20. 初始化渲染相关设置
+
+```cpp
+#if WITH_ENGINE
+	extern ENGINE_API void InitializeRenderingCVarsCaching();
+	InitializeRenderingCVarsCaching();
+#endif
+```
+
+21. 初始化 Oodle, 加载 PreInit 模块
+
+```cpp
+// init Oodle here
+FOodleDataCompression::StartupPreInit();
+
+{
+    SCOPED_BOOT_TIMING("LoadPreInitModules");
+    LoadPreInitModules();
+}
+```
+
+> `Oodle` 是一套高效的压缩工具集,用于压缩和解压缩游戏数据,以减少存储空间和提高数据传输速度
+
+22. 应用程序初始化
+
+```cpp
+// Start the application
+{
+    SCOPED_BOOT_TIMING("AppInit");
+    if (!AppInit())
+    {
+        return 1;
+    }
+}
+```
+
+23.  启动 IO 线程池
+
+```cpp
+if (FPlatformProcess::SupportsMultithreading())
+{
+    {
+        SCOPED_BOOT_TIMING("GIOThreadPool->Create");
+        GIOThreadPool = FQueuedThreadPool::Allocate();
+        int32 NumThreadsInThreadPool = FPlatformMisc::NumberOfIOWorkerThreadsToSpawn();
+        if (FPlatformProperties::IsServerOnly())
+        {
+            NumThreadsInThreadPool = 2;
+        }
+        verify(GIOThreadPool->Create(NumThreadsInThreadPool, 96 * 1024, TPri_AboveNormal, TEXT("IOThreadPool")));
+    }
+}
+```
+
+24. 初始化游戏系统设置
+
+```cpp
+#if WITH_ENGINE
+	{
+		SCOPED_BOOT_TIMING("System settings and cvar init");
+		// Initialize system settings before anyone tries to use it...
+		GSystemSettings.Initialize(bHasEditorToken);
+
+		// Apply renderer settings from console variables stored in the INI.
+		UE::ConfigUtilities::ApplyCVarSettingsFromIni(TEXT("/Script/Engine.RendererSettings"), *GEngineIni, ECVF_SetByProjectSetting);
+		UE::ConfigUtilities::ApplyCVarSettingsFromIni(TEXT("/Script/Engine.RendererOverrideSettings"), *GEngineIni, ECVF_SetByProjectSetting);
+		UE::ConfigUtilities::ApplyCVarSettingsFromIni(TEXT("/Script/Engine.StreamingSettings"), *GEngineIni, ECVF_SetByProjectSetting);
+		UE::ConfigUtilities::ApplyCVarSettingsFromIni(TEXT("/Script/Engine.GarbageCollectionSettings"), *GEngineIni, ECVF_SetByProjectSetting);
+		UE::ConfigUtilities::ApplyCVarSettingsFromIni(TEXT("/Script/Engine.NetworkSettings"), *GEngineIni, ECVF_SetByProjectSetting);
+#if WITH_EDITOR
+		UE::ConfigUtilities::ApplyCVarSettingsFromIni(TEXT("/Script/UnrealEd.CookerSettings"), *GEngineIni, ECVF_SetByProjectSetting);
+#endif
+
+#if !UE_SERVER
+		if (!bIsRunningAsDedicatedServer)
+		{
+			if (!bHasCommandletToken)
+			{
+				// Note: It is critical that resolution settings are loaded before the movie starts playing so that the window size and fullscreen state is known
+				UGameUserSettings::PreloadResolutionSettings();
+			}
+		}
+#endif
+	}
+```
+
+25.  `Slate` 应用程序初始化
+
+```cpp
+// Are we creating a slate application?
+bool bSlateApplication = !IsRunningDedicatedServer() && (bIsRegularClient || bHasEditorToken);
+if (bSlateApplication)
+{
+    if (FPlatformProcess::SupportsMultithreading() && !FParse::Param(FCommandLine::Get(), TEXT("RenderOffScreen")))
+    {
+        SCOPED_BOOT_TIMING("FPlatformSplash::Show()");
+        FPlatformSplash::Show();
+    }
+
+    // Init platform application
+    SCOPED_BOOT_TIMING("FSlateApplication::Create()");
+    FSlateApplication::Create();
+}
+else
+{
+    // If we're not creating the slate application there is some basic initialization
+    // that it does that still must be done
+    EKeys::Initialize();
+    FSlateApplication::InitializeCoreStyle();
+}
+```
+
+26. 处理 Shader
+
+### PreInitPostStartupScreen
+
+1. 启动加载界面
+
+```cpp
+{
+	SCOPED_BOOT_TIMING("GetMoviePlayer()->SetupLoadingScreenFromIni");
+	// allow the movie player to load a sequence from the .inis (a PreLoadingScreen module could have already initialized a sequence, in which case
+	// it wouldn't have anything in it's .ini file)
+	GetMoviePlayer()->SetupLoadingScreenFromIni();
+}
+
+if (GetMoviePlayer()->HasEarlyStartupMovie())
+{
+	SCOPED_BOOT_TIMING("EarlyStartupMovie");
+	GetMoviePlayer()->Initialize(SlateRendererSharedRef.Get(), FPreLoadScreenManager::Get() ? FPreLoadScreenManager::Get()->GetRenderWindow() : nullptr);
+
+    /**
+    * Do Something 
+    */
+}
+```
+
+2. 加载插件和项目的 `PreEarlyLoadingScreen` 模块
+
+```cpp
+if (!IProjectManager::Get().LoadModulesForProject(ELoadingPhase::PreEarlyLoadingScreen) || !IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::PreEarlyLoadingScreen))
+{
+    return 1;
+}
+```
+
+3. 挂载 PAK 文件和应用配置
+
+```cpp
+//Now that our EarlyStartupScreen is finished, lets take the necessary steps to mount paks, apply .ini cvars, and open the shader libraries if we installed content we expect to handle
+//If using a bundle manager, assume its handling all this stuff and that we don't have to do it.
+if (BundleManager == nullptr || BundleManager->IsNullInterface() || !BundleManager->SupportsEarlyStartupPatching())
+{
+    // Mount Paks that were installed during EarlyStartupScreen
+    if (FCoreDelegates::OnMountAllPakFiles.IsBound() && FPaths::HasProjectPersistentDownloadDir() )
+    {
+    // do something
+    }
+    // do something
+}
+```
+
+4. 各种初始化
+
+```cpp
+    InitGameTextLocalization();
+    FPackageName::RegisterShortPackageNamesForUObjectModules();
+    FModuleManager::Get().LoadModule("AssetRegistry");
+    IPackageResourceManager::Initialize();
+    IBulkDataRegistry::Initialize();
+    
+    FDelayedAutoRegisterHelper::RunAndClearDelayedAutoRegisterDelegates(EDelayedRegisterRunPhase::PreObjectSystemReady);
+    
+    ProcessNewlyLoadedUObjects();
+#if WITH_EDITOR
+    if (!UE::Virtualization::ShouldInitializePreSlate())
+    {
+        // Explicit initialization of the virtualization system, after slate has initialized and we can show error dialogs.
+        UE::Virtualization::Initialize(UE::Virtualization::EInitializationFlags::None);
+    }
+#endif //WITH_EDITOR
+
+    // Ensure game localization has loaded before we continue
+    FTextLocalizationManager::Get().WaitForAsyncTasks();
+
+    FDelayedAutoRegisterHelper::RunAndClearDelayedAutoRegisterDelegates(EDelayedRegisterRunPhase::ObjectSystemReady);
+    
+    FPackageLocalizationManager::Get().PerformLazyInitialization();
+    
+    {
+        SCOPED_BOOT_TIMING("InitDefaultMaterials etc");
+        // Default materials may have been loaded due to dependencies when loading
+        // classes and class default objects. If not, do so now.
+        UMaterialInterface::InitDefaultMaterials();
+        UMaterialInterface::AssertDefaultMaterialsExist();
+        UMaterialInterface::AssertDefaultMaterialsPostLoaded();
+    }
+
+    // Initialize the texture streaming system (needs to happen after RHIInit and ProcessNewlyLoadedUObjects).
+    IStreamingManager::Get();
+```
+
+5. 加载核心模块
+
+```cpp
+SCOPED_BOOT_TIMING("LoadStartupCoreModules");
+if (!LoadStartupCoreModules())
+{
+    // At least one startup module failed to load, return 1 to indicate an error
+    return 1;
+}
+```
+
+> 就是分别加载 `Core`、`Networking` 和 `SlateCore` 等模块
+
+6. 启动渲染线程
+
+```cpp
+SCOPED_BOOT_TIMING("PostInitRHI etc");
+PostInitRHI();
+
+if (GUseThreadedRendering)
+{
+    if (GRHISupportsRHIThread)
+    {
+        const bool DefaultUseRHIThread = true;
+        GUseRHIThread_InternalUseOnly = DefaultUseRHIThread;
+        if (FParse::Param(FCommandLine::Get(), TEXT("rhithread")))
+        {
+            GUseRHIThread_InternalUseOnly = true;
+        }
+        else if (FParse::Param(FCommandLine::Get(), TEXT("norhithread")))
+        {
+            GUseRHIThread_InternalUseOnly = false;
+        }
+    }
+    StartRenderingThread();
+}
+```
+
+7. 加载开始模块
+
+```cpp
+if (!LoadStartupModules())
+{
+    // At least one startup module failed to load, return 1 to indicate an error
+    return 1;
+}
+```
+
+> 这里就是分别加载 `PreDefault`、 `Default` 和 `PostDefault` 三个阶段的模块
+
+8. 对 `Commandled` 提供支持,仅在非编辑器和 DS 情况下运行
+
+```cpp
+if (!bHasEditorToken && !IsRunningDedicatedServer())
+{
+	UClass* CommandletClass = nullptr;
+
+	if (!bIsRegularClient)
+	{
+		checkf(PRIVATE_GIsRunningCommandlet, TEXT("This should have been set in PreInitPreStartupScreen"));
+
+		CommandletClass = Cast<UClass>(StaticFindFirstObject(UClass::StaticClass(), *Token, EFindFirstObjectOptions::None, ELogVerbosity::Warning, TEXT("looking for commandlet")));
+
+        /**
+         * Do Something
+         */
+    }
+}
+```
+
+## Init
+
+```cpp
+#if WITH_EDITOR
+    if (GIsEditor)
+    {
+        ErrorLevel = EditorInit(GEngineLoop);
+    }
+    else
+#endif
+    {
+        ErrorLevel = EngineInit();
+    }
+```
+
+1. 引擎初始化
+
+```cpp
+int32 ErrorLevel = EngineLoop.Init();
+if( ErrorLevel != 0 )
+{
+    FPlatformSplash::Hide();
+    return 0;
+}
+```
+
+2. 初始化 Actor 文件夹和 Misc 功能
+
+```cpp
+FActorFolders::Get();
+
+// Initialize the misc editor
+FUnrealEdMisc::Get().OnInit();
+```
+
+3. 设置退出时的回调,清理
+
+```cpp
+FCoreDelegates::OnExit.AddLambda([]()
+{
+    // Shutdown the global static mode manager
+    if (Internal::GetGlobalModeManager().IsValid())
+    {
+        GLevelEditorModeTools().SetDefaultMode(FBuiltinEditorModes::EM_Default);
+        Internal::GetGlobalModeManager().Reset();
+    }
+});
+```
+
+4. 加载保存的文件夹历史记录
+
+```cpp
+// Prime our array of default directories for loading and saving content files to
+FEditorDirectories::Get().LoadLastDirectories();
+```
+
+5. 启动主编辑器窗口
+
+```cpp
+IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked<IMainFrameModule>(TEXT("MainFrame"));
+if (!MainFrameModule.IsWindowInitialized())
+{
+    if (FSlateApplication::IsInitialized())
+    {
+        MainFrameModule.CreateDefaultMainFrame(bStartImmersive, bStartPIE);
+    }
+    else
+    {
+        RequestEngineExit(TEXT("Slate Application terminated or not initialized for MainFrame"));
+        return 1;
+    }
+}
+```
+
+6. 检查项目文件状态
+
+```cpp
+// Prompt to update the game project file to the current version, if necessary
+if ( FPaths::IsProjectFilePathSet() )
+{
+    FGameProjectGenerationModule::Get().CheckForOutOfDateGameProjectFile();
+    FGameProjectGenerationModule::Get().CheckAndWarnProjectFilenameValid();
+}
+```
+
+> 确保项目文件是最新格式。
+> 检查项目文件名是否合法,并在必要时发出警告。
+
+## Tick
+
+# 挂载
+
+## 从初始化中查找 Pak 挂载代码
+
+在 `PreInitPreStartupScreen` 函数中存在着使用 `LaunchCheckForFileOverride` 方法
+
+```cpp
+SCOPED_BOOT_TIMING("LaunchCheckForFileOverride");
+if (LaunchCheckForFileOverride(CmdLine, bFileOverrideFound) == false)
+{
+    // if it failed, we cannot continue
+    return 1;
+}
+```
+
+在 `LaunchCheckForFileOverride` 中会创建 `PakFile` 的文件接口
+
+```cpp
+IPlatformFile* PlatformFile = ConditionallyCreateFileWrapper(TEXT("PakFile"), CurrentPlatformFile, CmdLine);
+if (PlatformFile)
+{
+    CurrentPlatformFile = PlatformFile;
+    FPlatformFileManager::Get().SetPlatformFile(*CurrentPlatformFile);
+}
+```
+ 
+在 `ConditionallyCreateFileWrapper` 会根据名称检索对应的文件接口类,并将其初始化 `Initialize`
+
+```cpp
+static IPlatformFile* ConditionallyCreateFileWrapper(const TCHAR* Name, IPlatformFile* CurrentPlatformFile, const TCHAR* CommandLine, bool* OutFailedToInitialize = nullptr, bool* bOutShouldBeUsed = nullptr )
+{
+	// 做一些条件判断
+	IPlatformFile* WrapperFile = FPlatformFileManager::Get().GetPlatformFile(Name);
+	if (WrapperFile != nullptr && WrapperFile->ShouldBeUsed(CurrentPlatformFile, CommandLine))
+	{
+    	// 做一些条件判断
+		if (WrapperFile->Initialize(CurrentPlatformFile, CommandLine) == false)
+		{
+	        // 做一些条件判断 设置 bOutShouldBeUsed 和 OutFailedToInitialize 的值
+		}
+	}
+	// 做一些条件判断
+	return WrapperFile;
+}
+```
+
+对应的类就是 `FPakPlatformFile`
+
+```cpp
+class FPakPlatformFile : public IPlatformFile
+```
+
+在 `FPakPlatformFile::Initialize` 中
+
+- 指定特定类型的文件
+
+```cpp
+	// Extensions for file types that should only ever be in a pak file. Used to stop unnecessary access to the lower level platform file
+	ExcludedNonPakExtensions.Add(TEXT("uasset"));
+	ExcludedNonPakExtensions.Add(TEXT("umap"));
+	ExcludedNonPakExtensions.Add(TEXT("ubulk"));
+	ExcludedNonPakExtensions.Add(TEXT("uexp"));
+	ExcludedNonPakExtensions.Add(TEXT("uptnl"));
+	ExcludedNonPakExtensions.Add(TEXT("ushaderbytecode"));
+```
+
+- 初始化全局容器(`IoStore` 和 `PackageStore`)
+
+```cpp
+    if (ShouldCheckPak())
+    {
+        ensure(CheckIoStoreContainerBlockSignatures(*GlobalUTocPath));
+    }
+
+    FIoDispatcher& IoDispatcher = FIoDispatcher::Get();
+    IoDispatcherFileBackend = CreateIoDispatcherFileBackend();
+    IoDispatcher.Mount(IoDispatcherFileBackend.ToSharedRef());
+    PackageStoreBackend = MakeShared<FFilePackageStoreBackend>();
+    FPackageStore::Get().Mount(PackageStoreBackend.ToSharedRef());
+```
+
+- 查找并挂载 `Pak` 文件
+
+```cpp
+	// Find and mount pak files from the specified directories.
+	TArray<FString> PakFolders;
+	GetPakFolders(FCommandLine::Get(), PakFolders);
+	MountAllPakFiles(PakFolders, *StartupPaksWildcard);
+```
+
+- 绑定事件,其他模块可以通过事件触发 回调 `FPakPlatformFile::MountAllPakFiles`
+
+```cpp
+FCoreDelegates::OnMountAllPakFiles.BindRaw(this, &FPakPlatformFile::MountAllPakFiles);
+FCoreDelegates::MountPak.BindRaw(this, &FPakPlatformFile::HandleMountPakDelegate);
+FCoreDelegates::OnUnmountPak.BindRaw(this, &FPakPlatformFile::HandleUnmountPakDelegate);
+FCoreDelegates::OnOptimizeMemoryUsageForMountedPaks.BindRaw(this, &FPakPlatformFile::OptimizeMemoryUsageForMountedPaks);
+
+FCoreDelegates::OnFEngineLoopInitComplete.AddRaw(this, &FPakPlatformFile::OptimizeMemoryUsageForMountedPaks);
+```
+
+在 `PreInitPostStartupScreen` 函数中使用 `OnMountAllPakFiles` 事件来触发挂载 `Pak` 文件的逻辑
+
+```cpp
+// Mount Paks that were installed during EarlyStartupScreen
+if (FCoreDelegates::OnMountAllPakFiles.IsBound() && FPaths::HasProjectPersistentDownloadDir() )
+{
+    SCOPED_BOOT_TIMING("MountPaksAfterEarlyStartupScreen");
+
+    FString InstalledGameContentDir = FPaths::Combine(*FPaths::ProjectPersistentDownloadDir(), TEXT("InstalledContent"), FApp::GetProjectName(), TEXT("Content"), TEXT("Paks"));
+    FPlatformMisc::AddAdditionalRootDirectory(FPaths::Combine(*FPaths::ProjectPersistentDownloadDir(), TEXT("InstalledContent")));
+
+    TArray<FString> PakFolders;
+    PakFolders.Add(InstalledGameContentDir);
+    FCoreDelegates::OnMountAllPakFiles.Execute(PakFolders);
+
+    // Look for any plugins installed during EarlyStartupScreen
+    IPluginManager::Get().RefreshPluginsList();
+    IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::PreEarlyLoadingScreen);
+}
+```
+
+通过 `FCoreDelegates::MountPak` 可以单独挂载一个 `Pak` 文件,也可以为这个 `Pak` 设置优先级,如果没有设置优先级会通过 `GetPakOrderFromPakFilePath(PakFilename)` 计算优先级
+
+```cpp
+int32 FPakPlatformFile::GetPakOrderFromPakFilePath(const FString& PakFilePath)
+{
+	if (PakFilePath.StartsWith(FString::Printf(TEXT("%sPaks/%s-"), *FPaths::ProjectContentDir(), FApp::GetProjectName())))
+	{
+		return 4;
+	}
+	else if (PakFilePath.StartsWith(FPaths::ProjectContentDir()))
+	{
+		return 3;
+	}
+	else if (PakFilePath.StartsWith(FPaths::EngineContentDir()))
+	{
+		return 2;
+	}
+	else if (PakFilePath.StartsWith(FPaths::ProjectSavedDir()))
+	{
+		return 1;
+	}
+
+	return 0;
+}
+```
+
+换句话说,如果在 `FCoreDelegates::MountPak` 挂载的时候想要自己的这个 Pak 优先级最高,可以设置优先级为 4 即可
+
+
 # 网络热更
 
 1. 解压解密和压缩加密数据