【UE4源代码分析】-002 UE4中的配置文件

时间:2022-02-23 17:46:38

1、UE4配置文件类型

 UE4中,大量使用的配置文件类型并不是xml与json等新兴配置文件类型,而是使用相对古老的ini配置文件。
 在UE4的目录中,专门有Config目录用于存放使用的ini配置文件。

2、配置文件的类型

 在Config目录下有很多各种各样的配置文件,各个配置文件基本上代表的是一类配置。通过查看\Engine\Source\Runtime\Core\Public\Misc\ConfigCacheIni.h文件,可以帮助我们判断各个配置文件的作用。
 在枚举类EConfigFileHierarchy中,对各个ini文件以及各个位置的ini配置文件的用途做了基本说明。

enum class EConfigFileHierarchy : uint8
{
	// Engine/Config/Base.ini
	AbsoluteBase = 0,

	// Engine/Config/*.ini
	EngineDirBase,
	// Engine/Config/Platform/BasePlatform* ini
	EngineDir_BasePlatform,
	// Engine/Config/NotForLicensees/*.ini
	EngineDirBase_NotForLicensees,
	// Engine/Config/NoRedist/*.ini -Not supported at this time.
	EngineDirBase_NoRedist,

	// Game/Config/*.ini
	GameDirDefault,
	// Game/Config/DedicatedServer*.ini
	GameDirDedicatedServer,
	// Game/Config/NotForLicensees/*.ini
	GameDirDefault_NotForLicensees,
	// Game/Config/NoRedist*.ini
	GameDirDefault_NoRedist,

	// Engine/Config/PlatformName/PlatformName*.ini
	EngineDir_Platform,
	// Engine/Config/NotForLicensees/PlatformName/PlatformName*.ini
	EngineDir_Platform_NotForLicensees,
	// Engine/Config/NoRedist/PlatformName/PlatformName*.ini
	EngineDir_Platform_NoRedist,

	// Game/Config/PlatformName/PlatformName*.ini
	GameDir_Platform,
	// Game/Config/NotForLicensees/PlatformName/PlatformName*.ini
	GameDir_Platform_NotForLicensees,
	// Game/Config/NoRedist/PlatformName/PlatformName*.ini
	GameDir_Platform_NoRedist,

	// <UserSettingsDir|AppData>/Unreal Engine/Engine/Config/User*.ini
	UserSettingsDir_EngineDir_User,
	// <UserDir|Documents>/Unreal Engine/Engine/Config/User*.ini
	UserDir_User,
	// Game/Config/User*.ini
	GameDir_User,

	// Number of config files in hierarchy.
	NumHierarchyFiles,
};

3、ini文件的加载和解析

 UE4中,使用类FConfigCacheIni来加载和管理系统中所使用的所有ini文件,可以通过该类来读取和设置ini文件中相应键值对。

3.1 FConfigCacheIni-配置文件系统

 首先,我们来看一下FConfigCacheIni类的定义。

// Set of all cached config files.
class CORE_API FConfigCacheIni : public TMap<FString,FConfigFile>
{
    //接口函数已经省略,请自行查看代码

private:
	/** true if file operations should not be performed */
	bool bAreFileOperationsDisabled;

	/** true after the base .ini files have been loaded, and GConfig is generally "ready for use" */
	bool bIsReadyForUse;
	
	/** The type of the cache (basically, do we call Flush in the destructor) */
	EConfigCacheType Type;
};

 可以看出,FConfigCacheIni实际上是由一组文件名-配置文件FConfigFile组成的Map加上表征配置文件系统状态和类型的变量bool bAreFileOperationsDisabled、bool bIsReadyForUse、EConfigCacheType Type组成的。
 FConfigCacheIni类的访问具体键值的接口型如:

void SetInt//设置整型数值,通过指定ini文件、指定区段、指定键名称,设置该键为int值
(
	const TCHAR*		Section,//区段名
	const TCHAR*		Key,//键名
	int32					Value,//值
	const FString&	Filename//ini文件名
);
bool GetInt//通过ini文件名,区段名,键名称获取该键对应的int值
(
	const TCHAR*		Section,//区段名
	const TCHAR*		Key,//键名
	int32&				Value,//输出int值
	const FString&	Filename//ini文件名
);

 TConfigCacheIni类还有几个特殊的静态成员函数,用于执行整个配置文件缓存系统的初始化和配置文件的加载工作。
 例如,在static void InitializeConfigSystem()中,完成了全局变量FConfigCacheIni*GConfig = nullptr所指向对象的创建和赋值工作。之后调用LoadGlobalIniFile、LoadExternalIniFile、LoadLocalIniFile、LoadConsoleVariablesFromINI进行ini文件的加载。

// Static helper functions

	static void InitializeConfigSystem();

	
	static bool LoadGlobalIniFile(FString& FinalIniFilename, const TCHAR* BaseIniName, const TCHAR* Platform=NULL, bool bForceReload=false, bool bRequireDefaultIni=false, bool bAllowGeneratedIniWhenCooked=true, const TCHAR* GeneratedConfigDir = *FPaths::GeneratedConfigDir());

	
	static bool LoadLocalIniFile(FConfigFile& ConfigFile, const TCHAR* IniName, bool bIsBaseIniName, const TCHAR* Platform=NULL, bool bForceReload=false);

	
	static bool LoadExternalIniFile(FConfigFile& ConfigFile, const TCHAR* IniName, const TCHAR* EngineConfigDir, const TCHAR* SourceConfigDir, bool bIsBaseIniName, const TCHAR* Platform=NULL, bool bForceReload=false, bool bWriteDestIni=false, bool bAllowGeneratedIniWhenCooked = true, const TCHAR* GeneratedConfigDir = *FPaths::GeneratedConfigDir());

	
	static void LoadConsoleVariablesFromINI();

3.2 FConfigFile-配置文件

 FConfigFile类用于表示一个ini配置文件。一个ini配置文件可以有多个区段组成,每个区段有自己的名称,因此,一个配置文件至少包含一个名称-区段的映射表。同时,一个配置文件还具有自己的文件名,配置文件类型的属性。

// One config file.

class FConfigFile : public TMap<FString,FConfigSection>
{
public:
	bool Dirty, NoSave;

	/** The name of this config file */	
	FName Name;

	// The collection of source files which were used to generate this file.
	FConfigFileHierarchy SourceIniHierarchy;

	/** The untainted config file which contains the coalesced base/default options. I.e. No Saved/ options*/
	FConfigFile* SourceConfigFile;

	/** Key to the cache to speed up ini parsing */
	FString CacheKey;

#if ALLOW_INI_OVERRIDE_FROM_COMMANDLINE
	/** The collection of overrides which stemmed from the commandline */
	TArray<FConfigCommandlineOverride> CommandlineOptions;
#endif // ALLOW_INI_OVERRIDE_FROM_COMMANDLINE
};

 通过一个配置文件对象,可以对该对象所表示的配置文件中的值进行读取和写入。读取和写入函数型如:

CORE_API void SetString( const TCHAR* Section, const TCHAR* Key, const TCHAR* Value );
CORE_API bool GetString( const TCHAR* Section, const TCHAR* Key, FString& Value ) const;

 与上节中FConfigCacheIni类的接口不同的是,FConfigFile类的接口不在需要指定ini文件名。

3.3 FConfigSection-ini文件中的区段

 一个ini文件的区段,是由一组或多组键值对组成的,所以从FConfigSection类是一个由键名称-值组成的映射表。注意它的MultiFind成员函数,从它的实现我们可以看出,UE4的配置文件中,是支持在同一个区段中,一个键的名称对应多个不同的值的,也就是说存在1-N的映射关系。

typedef TMultiMap<FName,FConfigValue> FConfigSectionMap;

// One section in a config file.
class FConfigSection : public FConfigSectionMap
{
public:
	/**
	* Check whether the input string is surrounded by quotes
	*
	* @param Test The string to check
	*
	* @return true if the input string is surrounded by quotes
	*/
	static bool HasQuotes( const FString& Test );
	bool operator==( const FConfigSection& Other ) const;
	bool operator!=( const FConfigSection& Other ) const;

	// process the '+' and '.' commands, takingf into account ArrayOfStruct unique keys
	void CORE_API HandleAddCommand(FName Key, const FString& Value, bool bAppendValueIfNotArrayOfStructsKeyUsed);

	template<typename Allocator> 
	void MultiFind(const FName Key, TArray<FConfigValue, Allocator>& OutValues, const bool bMaintainOrder = false) const
	{
		FConfigSectionMap::MultiFind(Key, OutValues, bMaintainOrder);
	}

	template<typename Allocator> 
	void MultiFind(const FName Key, TArray<FString, Allocator>& OutValues, const bool bMaintainOrder = false) const
	{
		for (const TPair<FName, FConfigValue>& Pair : Pairs)
		{
			if (Pair.Key == Key)
			{
				OutValues.Add(Pair.Value.GetValue());
			}
		}

		if (bMaintainOrder)
		{
			Algo::Reverse(OutValues);
		}
	}

	// look for "array of struct" keys for overwriting single entries of an array
	TMap<FName, FString> ArrayOfStructKeys;
};

3.4 FConfigValue-配置的值

 UE4配置文件系统FConfigCacheIni由多个FConfigFile组成,FConfigFile有多个FConfigSection组成,FConfigSection则由多个FConfigValue组成。FConfigValue是保存配置文件值的字面值的地方,也就意味着,在整个UE4的配置文件系统中,将所有从配置文件中读取的值都保存成了字符串,在需要使用的时候再通过相应的接口转变成所需要的类型,比如int等类型。

struct FConfigValue
{


	// Returns the ini setting with any macros expanded out
	const FString& GetValue() const 
	{ 
#if WITH_EDITOR
		bRead = true; 
#endif
		return (ExpandedValue.Len() > 0 ? ExpandedValue : SavedValue); 
	}

	// Returns the original ini setting without macro expansion
	const FString& GetSavedValue() const 
	{ 
#if WITH_EDITOR
		bRead = true; 
#endif
		return SavedValue; 
	}

private:
	/** Internal version of ExpandValue that expands SavedValue into ExpandedValue, or produces an empty ExpandedValue if no expansion occurred. */
	void ExpandValueInternal()
	{
		if (!ExpandValue(SavedValue, ExpandedValue))
		{
			ExpandedValue.Empty();
		}
	}

	FString SavedValue;
	FString ExpandedValue;
#if WITH_EDITOR
	mutable bool bRead; // has this value been read since the config system started
#endif
};

 从中可以看出,FConfigValue保存的是FString类型的字符串。

4、UE4配置系统结构图

UE4系统配置文件系统总体结构图如下所示。

【UE4源代码分析】-002 UE4中的配置文件