UE4 Ini配置文件的层级

本文的重点:理解UE4的“Ini层级”概念,以及找到其对应代码,与观察运行结果。

官方文档中介绍的“层级”概念

官方文档icon-default.png?t=N7T8https://dev.epicgames.com/documentation/en-us/unreal-engine/configuration-files-in-unreal-engine?application_version=5.4 在UE4的官方文档中,搜索“Configuration File Hierarchy”小节,可以看到Ini配置文件的优先级层级如下:

Category configuration files are loaded in the following order:

  1. Engine/Config/Base.ini
  2. Engine/Config/Base<CATEGORY>.ini
  3. Engine/Config/<PLATFORM>/Base<PLATFORM><CATEGORY>.ini
  4. Engine/Platforms/<PLATFORM>/Config/Base<PLATFORM><CATEGORY>.ini
  5. <PROJECT_DIRECTORY>/Config/Default<CATEGORY>.ini
  6. Engine/Config/<PLATFORM>/<PLATFORM><CATEGORY>.ini
  7. Engine/Platforms/<PLATFORM>/Config/<PLATFORM><CATEGORY>.ini
  8. <PROJECT_DIRECTORY>/Config/<PLATFORM>/<PLATFORM><CATEGORY>.ini
  9. <PROJECT_DIRECTORY>/Platforms/<PLATFORM>/Config/<PLATFORM><CATEGORY>.ini
  10. <LOCAL_APP_DATA>/Unreal Engine/Engine/Config/User<CATEGORY>.ini
  11. <MY_DOCUMENTS>/Unreal Engine/Engine/Config/User<CATEGORY>.ini
  12. <PROJECT_DIRECTORY>/Config/User<CATEGORY>.ini

越往后的越高优先级,它对应的代码是:

/**
 * Structure to define all the layers of the config system. Layers can be expanded by expansion files (NoRedist, etc), or by ini platform parents
 * (coming soon from another branch)
 */
struct FConfigLayer
{
	// Used by the editor to display in the ini-editor
	const TCHAR* EditorName;
	// Path to the ini file (with variables)
	const TCHAR* Path;
	// Path to the platform extension version
	const TCHAR* PlatformExtensionPath;
	// Special flag
	EConfigLayerFlags Flag;

} GConfigLayers[] =
{
	/**************************************************
	**** CRITICAL NOTES
	**** If you change this array, you need to also change EnumerateConfigFileLocations() in ConfigHierarchy.cs!!!
	**** And maybe UObject::GetDefaultConfigFilename(), UObject::GetGlobalUserConfigFilename()
	**************************************************/

	// Engine/Base.ini
	{ TEXT("AbsoluteBase"),				TEXT("{ENGINE}Base.ini"), TEXT(""), EConfigLayerFlags::Required },
	
	// Engine/Base*.ini
	{ TEXT("Base"),						TEXT("{ENGINE}{ED}{EF}Base{TYPE}.ini") },
	// Engine/Platform/BasePlatform*.ini
	{ TEXT("BasePlatform"),				TEXT("{ENGINE}{ED}{PLATFORM}/{EF}Base{PLATFORM}{TYPE}.ini"), TEXT("{EXTENGINE}/{ED}{EF}Base{PLATFORM}{TYPE}.ini"),  },
	// Project/Default*.ini
	{ TEXT("ProjectDefault"),			TEXT("{PROJECT}{ED}{EF}Default{TYPE}.ini"), TEXT(""), EConfigLayerFlags::AllowCommandLineOverride | EConfigLayerFlags::GenerateCacheKey },
	// Engine/Platform/Platform*.ini
	{ TEXT("EnginePlatform"),			TEXT("{ENGINE}{ED}{PLATFORM}/{EF}{PLATFORM}{TYPE}.ini"), TEXT("{EXTENGINE}/{ED}{EF}{PLATFORM}{TYPE}.ini") },
	// Project/Platform/Platform*.ini
	{ TEXT("ProjectPlatform"),			TEXT("{PROJECT}{ED}{PLATFORM}/{EF}{PLATFORM}{TYPE}.ini"), TEXT("{EXTPROJECT}/{ED}{EF}{PLATFORM}{TYPE}.ini") },
	
	// UserSettings/.../User*.ini
	{ TEXT("UserSettingsDir"),			TEXT("{USERSETTINGS}Unreal Engine/Engine/Config/User{TYPE}.ini") },
	// UserDir/.../User*.ini
	{ TEXT("UserDir"),					TEXT("{USER}Unreal Engine/Engine/Config/User{TYPE}.ini") },
	// Project/User*.ini
	{ TEXT("GameDirUser"),				TEXT("{PROJECT}User{TYPE}.ini"), TEXT(""), EConfigLayerFlags::GenerateCacheKey },
};

(代码块1)

其中的 {ED} {EF} 是用于替换的记号,{ED}表示文件夹的前缀,{EF}表示文件的前缀,代码位于

static FString GetExpansionPath(const FConfigLayerExpansion& Expansion, const FString& LayerPath, bool bHasPlatformTag)
{
	// replace the expansion tags
	FString ExpansionPath = LayerPath.Replace(TEXT("{ED}"), Expansion.DirectoryPrefix, ESearchCase::CaseSensitive);
	ExpansionPath = ExpansionPath.Replace(TEXT("{EF}"), Expansion.FilePrefix, ESearchCase::CaseSensitive);

替换的规则见 GConfigLayerExpansions[] 。因为代码很多很杂,接下来我找到了重点的代码进行学习和罗列。

代码示意图

我找到了重点代码,绘制了如下调用关系图:

其中的 “Final” 或说 “Dest” Ini的概念,对应的接口文档是:

https://docs.unrealengine.com/4.27/en-US/API/Runtime/Core/Misc/FConfigCacheIni/GetDestIniFilename/

其中MarkC处 AddStaticLayersToHierarchy() 的含义是:

一、第一层For循环:遍历各层级

    // go over all the config layers
    for (int32 LayerIndex = 0; LayerIndex < UE_ARRAY_COUNT(GConfigLayers); LayerIndex++)

见前文“代码块1”;

二、第二层For循环:遍历各平台

                for (int PlatformIndex = 0; PlatformIndex < NumPlatforms; PlatformIndex++)

例如安卓。实测没有看到 Android 等字样,不细究。

调试“加载Ini的过程”

1、在 void FConfigCacheIni::InitializeConfigSystem() 的最后一行 FCoreDelegates::ConfigReadyForUse.Broadcast() 处下断点;

2、以调试Game的方式(UE4如何直接调试Game-CSDN博客)进行调试。

UE4加载Ini的堆栈是:

FConfigCacheIni::InitializeConfigSystem() ConfigCacheIni.cpp:3908
FEngineLoop::AppInit() LaunchEngineLoop.cpp:5716
FEngineLoop::PreInitPreStartupScreen(const wchar_t *) LaunchEngineLoop.cpp:2083
FEngineLoop::PreInit(const wchar_t *) LaunchEngineLoop.cpp:3593
[Inlined] FEngineLoop::PreInit(int, wchar_t **, const wchar_t *) LaunchEngineLoop.cpp:1104
wmain(int, wchar_t **) UnrealPak.cpp:13

UE_LOG(LogTemp, Warning, TEXT("MyCodeTrace:ConfigFile.Name:>>>>  %s"), * ConfigFile.Name.ToString());
// ConfigFile.Name
for(auto each 	 /* TMap<FString,FConfigSection> */ : ConfigFile.SourceIniHierarchy)
	{
			UE_LOG(LogTemp, Warning, TEXT("MyCodeTrace:WatchStaticLayers  %d : %s"), each.Key,  * each.Value.Filename);
	}
	UE_LOG(LogTemp, Warning, TEXT("MyCodeTrace:ConfigFile.Name:<<<<  %s"), * ConfigFile.Name.ToString());

通过添加日志的方式,如上,观察到 MarkC处 AddStaticLayersToHierarchy() 处添加的 Layers 是这些内容:

0 : ../../../Engine/Config/Base.ini
10000 : ../../../Engine/Config/BaseInput.ini
10200 : ../../../Engine/Config/ShippableBaseInput.ini
10300 : ../../../Engine/Config/NotForLicensees/BaseInput.ini
10400 : ../../../Engine/Config/NotForLicensees/ShippableBaseInput.ini
10500 : ../../../Engine/Config/NoRedist/BaseInput.ini
10600 : ../../../Engine/Config/NoRedist/ShippableBaseInput.ini
20000 : ../../../Engine/Config/Windows/BaseWindowsInput.ini
20200 : ../../../Engine/Config/Windows/ShippableBaseWindowsInput.ini
20300 : ../../../Engine/Config/NotForLicensees/Windows/BaseWindowsInput.ini
20400 : ../../../Engine/Config/NotForLicensees/Windows/ShippableBaseWindowsInput.ini
20500 : ../../../Engine/Config/NoRedist/Windows/BaseWindowsInput.ini
20600 : ../../../Engine/Config/NoRedist/Windows/ShippableBaseWindowsInput.ini
30000 : ../../../Engine/Programs/UnrealPak/Config/DefaultInput.ini
30200 : ../../../Engine/Programs/UnrealPak/Config/ShippableDefaultInput.ini
30300 : ../../../Engine/Programs/UnrealPak/Config/NotForLicensees/DefaultInput.ini
30400 : ../../../Engine/Programs/UnrealPak/Config/NotForLicensees/ShippableDefaultInput.ini
30500 : ../../../Engine/Programs/UnrealPak/Config/NoRedist/DefaultInput.ini
30600 : ../../../Engine/Programs/UnrealPak/Config/NoRedist/ShippableDefaultInput.ini
40000 : ../../../Engine/Config/Windows/WindowsInput.ini
40200 : ../../../Engine/Config/Windows/ShippableWindowsInput.ini
40300 : ../../../Engine/Config/NotForLicensees/Windows/WindowsInput.ini
40400 : ../../../Engine/Config/NotForLicensees/Windows/ShippableWindowsInput.ini
40500 : ../../../Engine/Config/NoRedist/Windows/WindowsInput.ini
40600 : ../../../Engine/Config/NoRedist/Windows/ShippableWindowsInput.ini
50000 : ../../../Engine/Programs/UnrealPak/Config/Windows/WindowsInput.ini
50200 : ../../../Engine/Programs/UnrealPak/Config/Windows/ShippableWindowsInput.ini
50300 : ../../../Engine/Programs/UnrealPak/Config/NotForLicensees/Windows/WindowsInput.ini
50400 : ../../../Engine/Programs/UnrealPak/Config/NotForLicensees/Windows/ShippableWindowsInput.ini
50500 : ../../../Engine/Programs/UnrealPak/Config/NoRedist/Windows/WindowsInput.ini
50600 : ../../../Engine/Programs/UnrealPak/Config/NoRedist/Windows/ShippableWindowsInput.ini
60000 : C:/Users/wenjiezou/AppData/Local/Unreal Engine/Engine/Config/UserInput.ini
70000 : C:/Users/wenjiezou/Documents/Unreal Engine/Engine/Config/UserInput.ini
80000 : ../../../Engine/Programs/UnrealPak/Config/UserInput.ini

其中,最先的是

0 : ../../../Engine/Config/Base.ini
10000 : ../../../Engine/Config/BaseInput.ini

最后(最终 / 最优先生效)的是:

70000 : C:/Users/wenjiezou/Documents/Unreal Engine/Engine/Config/UserInput.ini
80000 : ../../../Engine/Programs/UnrealPak/Config/UserInput.ini

但实际上并不一定具有这些文件,例如实际上就不存在 C:/Users/wenjiezou/Documents/Unreal Engine/Engine/Config/UserInput.ini 这个文件。

实际上观察到的最终Ini文件是空的,例如 G:\St\EngineSource\Engine\Programs\UnrealPak\Saved\Config\Windows\GameUserSettings.ini 的内容是空行,原因可以参考源代码中的注释,当引擎初始化的时候,其实不应该产生变化,因此不需要输出最终Ini。

		// Check GIsInitialLoad since no INI changes that should be persisted could have occurred this early.
		// INI changes from code, environment variables, CLI parameters, etc should not be persisted. 
		if (!GIsInitialLoad && bWriteDestIni && (!FPlatformProperties::RequiresCookedData() || bAllowGeneratedIniWhenCooked)
			// We shouldn't save config files when in multiprocess mode,
			// otherwise we get file contention in XGE shader builds.
			&& !FParse::Param(FCommandLine::Get(), TEXT("Multiprocess")))
		{
			// Check the config system for any changes made to defaults and propagate through to the saved.
			ConfigFile.ProcessSourceAndCheckAgainstBackup();

			if (bNeedsWrite)
			{
				// if it was dirtied during the above function, save it out now
				ConfigFile.Write(DestIniFilename);
			}
		}

那么最终的GConfig是怎么样的呢?它其实是Map映射  { 最终Ini名 → FConfigFile },

其中,FConfigFile是映射  {  Section名  →  众字段  } ,如下图所示:

总结

UE4采用“Ini层级”的概念,让引擎、项目具有默认的配置值、逐项目的配置值、逐实际运行的实例的配置值(Saved下)、甚至是和本地环境相关的配置值。本文还找到了相关代码与运行结果进行学习。

  • 9
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值