bool UOnlineHotfixManager::HotfixPakIniFile(const FString& FileName) { FConfigFile* ConfigFile = GetConfigFile(FileName); ConfigFile->Combine(FileName); UE_LOG(LogHotfixManager, Log, TEXT("Hotfix merged INI (%s) found in a PAK file"), *FileName); FName IniFileName(*FileName, FNAME_Find); int32 NumObjectsReloaded = 0; const double StartTime = FPlatformTime::Seconds(); // Now that we have a list of classes to update, we can iterate objects and // reload if they match the INI file that was changed for (FObjectIterator It; It; ++It) { UClass* Class = It->GetClass(); if (Class->HasAnyClassFlags(CLASS_Config) && Class->ClassConfigName == IniFileName) { // Force a reload of the config vars It->ReloadConfig(); NumObjectsReloaded++; } } UE_LOG(LogHotfixManager, Log, TEXT("Updating config from %s took %f seconds reloading %d objects"), *FileName, FPlatformTime::Seconds() - StartTime, NumObjectsReloaded); return true; }
bool UOnlineHotfixManager::HotfixIniFile(const FString& FileName, const FString& IniData) { FConfigFile* ConfigFile = GetConfigFile(FileName); // Merge the string into the config file ConfigFile->CombineFromBuffer(IniData); TArray<UClass*> Classes; TArray<UObject*> PerObjectConfigObjects; int32 StartIndex = 0; int32 EndIndex = 0; // Find the set of object classes that were affected while (StartIndex >= 0 && StartIndex < IniData.Len() && EndIndex >= StartIndex) { // Find the next section header StartIndex = IniData.Find(TEXT("["), ESearchCase::IgnoreCase, ESearchDir::FromStart, StartIndex); if (StartIndex > -1) { // Find the ending section identifier EndIndex = IniData.Find(TEXT("]"), ESearchCase::IgnoreCase, ESearchDir::FromStart, StartIndex); if (EndIndex > StartIndex) { int32 PerObjectNameIndex = IniData.Find(TEXT(" "), ESearchCase::IgnoreCase, ESearchDir::FromStart, StartIndex); // Per object config entries will have a space in the name, but classes won't if (PerObjectNameIndex == -1 || PerObjectNameIndex > EndIndex) { if (IniData.StartsWith(TEXT("[/Script/"), ESearchCase::IgnoreCase)) { const int32 ScriptSectionTag = 9; // Snip the text out and try to find the class for that const FString PackageClassName = IniData.Mid(StartIndex + ScriptSectionTag, EndIndex - StartIndex - ScriptSectionTag); // Find the class for this so we know what to update UClass* Class = FindObject<UClass>(nullptr, *PackageClassName, true); if (Class) { // Add this to the list to check against Classes.Add(Class); } } } // Handle the per object config case by finding the object for reload else { const int32 Count = PerObjectNameIndex - StartIndex - 1; const FString PerObjectName = IniData.Mid(StartIndex + 1, Count); // Explicitly search the transient package (won't update non-transient objects) UObject* PerObject = FindObject<UObject>(ANY_PACKAGE, *PerObjectName, false); if (PerObject != nullptr) { PerObjectConfigObjects.Add(PerObject); } } StartIndex = EndIndex; } } } int32 NumObjectsReloaded = 0; const double StartTime = FPlatformTime::Seconds(); if (Classes.Num()) { // Now that we have a list of classes to update, we can iterate objects and reload for (FObjectIterator It; It; ++It) { UClass* Class = It->GetClass(); if (Class->HasAnyClassFlags(CLASS_Config)) { // Check to see if this class is in our list (yes, potentially n^2, but not in practice) for (int32 ClassIndex = 0; ClassIndex < Classes.Num(); ClassIndex++) { if (It->IsA(Classes[ClassIndex])) { // Force a reload of the config vars It->ReloadConfig(); NumObjectsReloaded++; break; } } } } } // Reload any PerObjectConfig objects that were affected for (auto ReloadObject : PerObjectConfigObjects) { ReloadObject->ReloadConfig(); NumObjectsReloaded++; } UE_LOG(LogHotfixManager, Log, TEXT("Updating config from %s took %f seconds and reloaded %d objects"), *FileName, FPlatformTime::Seconds() - StartTime, NumObjectsReloaded); return true; }