The cause of this problem is the Engine has code paths which register a new allocated object to Map without deleting the previous object.
In the repro project, whenever hitting Space Bar, the level blueprint calls Create Level Sequence Player and it constructs ULevelSequenceBurnInOptions which has UCLASS(config=EditorPerProjectUserSettings) specifiers. But in the packaged environment, EditorPerProjectUserSettings.ini could not found. As the result, FConfigContext::PrepareForLoad() function registers empty FConfigFile entry like;
ConfigFile = &ConfigSystem->Add(DestIniFilename, FConfigFile());
Note that the Add function calls new operator inside the method. But this entry is Empty and Num() returns zero, so when calling PrepareForLoad for EditorPerProjectUserSettings.ini from the second time cannot hit memory cache;
if (bLookForExistingFile) { // look up a file that already exists and matches the name FConfigFile* FoundConfigFile = ConfigSystem->KnownFiles.GetMutableFile(*BaseIniName); if (FoundConfigFile == nullptr) { FoundConfigFile = ConfigSystem->FindConfigFile(*DestIniFilename); //// @todo: this is test to see if we can simplify this to FindConfigFileWithBaseName always (if it never fires, we can) //check(FoundConfigFile == nullptr || FoundConfigFile == ConfigSystem->FindConfigFileWithBaseName(*BaseIniName)) } if (FoundConfigFile != nullptr && FoundConfigFile->Num() > 0) { ConfigFile = FoundConfigFile; bPerformLoad = false; return true; } }
Note that FoundConfigFile is not nullptr but FoundConfigFile->Num() returns zero. Then this function add new empty entry with the same key without removing the existing entry;
// setup ConfigFile to read into if one isn't already set if (ConfigFile == nullptr) { // first look for a KnownFile ConfigFile = ConfigSystem->KnownFiles.GetMutableFile(*BaseIniName); if (ConfigFile == nullptr) { check(!DestIniFilename.IsEmpty()); ConfigFile = &ConfigSystem->Add(DestIniFilename, FConfigFile()); } }
The possible solution is to remove the existing entry just before calling Add or regard ConfigFile is found but pretend to do loading.
if (bLookForExistingFile) { // ...skip... if (FoundConfigFile != nullptr && FoundConfigFile->Num() > 0) { ConfigFile = FoundConfigFile; bPerformLoad = false; return true; } + else if (FoundConfigFile != nullptr) + { + ConfigFile = FoundConfigFile; + bPerformLoad = true; + return true; + } }
This could be more refactored.
To reproduce this issue, need to set up the situation where some config ini file(s) cannot loaded and are not allowed to create.
I attached the repro project which can skip from step 1 to 3.
[Image Removed]
There's no existing public thread on this issue, so head over to Questions & Answers just mention UE-198500 in the post.
1 |
Component | UE - Foundation - Core |
---|---|
Affects Versions | 5.1.1, 5.2, 5.3.1 |
Target Fix | 5.6 |
Created | Oct 19, 2023 |
---|---|
Updated | Oct 18, 2024 |