void FConfigPropertyHelperDetails::OnPropertyValueChanged(UObject* Object, FPropertyChangedEvent& PropertyChangedEvent) { UClass* OwnerClass = ConfigEditorCopyOfEditProperty->GetOwnerClass(); if (Object->IsA(OwnerClass)) { const FString* FileName = ConfigFileAndPropertySourcePairings.FindKey(Object); if (FileName != nullptr) { const FString& ConfigIniName = *FileName; // We should set this up to work with the UObject Config system, its difficult as the outer object isnt of the same type // create a sandbox FConfigCache FConfigCacheIni Config(EConfigCacheType::Temporary); // add an empty file to the config so it doesn't read in the original file (see FConfigCacheIni.Find()) FConfigFile& NewFile = Config.Add(ConfigIniName, FConfigFile()); // save the object properties to this file OriginalProperty->GetOwnerClass()->GetDefaultObject()->SaveConfig(CPF_Config, *ConfigIniName, &Config); // Take the saved section for this object and have the config system process and write out the one property we care about. ensureMsgf(Config.Num() == 1, TEXT("UObject::UpdateDefaultConfig() caused more files than expected in the Sandbox config cache!")); TArray<FString> Keys; NewFile.GetKeys(Keys); const FString SectionName = Keys[0]; const FString PropertyName = ConfigEditorCopyOfEditProperty->GetName(); FString Value; ConfigEditorCopyOfEditProperty->ExportText_InContainer(0, Value, Object, Object, Object, 0); NewFile.SetString(*SectionName, *PropertyName, *Value); GConfig->SetString(*SectionName, *PropertyName, *Value, ConfigIniName); NewFile.UpdateSinglePropertyInSection(*ConfigIniName, *PropertyName, *SectionName); // reload the file, so that it refresh the cache internally. FString FinalIniFileName; GConfig->LoadGlobalIniFile(FinalIniFileName, *OriginalProperty->GetOwnerClass()->ClassConfigName.ToString(), NULL, true); // Update the CDO, as this change might have had an impact on it's value. OriginalProperty->GetOwnerClass()->GetDefaultObject()->ReloadConfig(); } } }
bool FPluginManager::ConfigureEnabledPlugins() { if(!bHaveConfiguredEnabledPlugins) { // Don't need to run this again bHaveConfiguredEnabledPlugins = true; // If a current project is set, check that we know about any plugin that's explicitly enabled const FProjectDescriptor *Project = IProjectManager::Get().GetCurrentProject(); const bool bHasProjectFile = Project != nullptr; // Get all the enabled plugin names TArray< FString > EnabledPluginNames; #if IS_PROGRAM // Programs can also define the list of enabled plugins in ini GConfig->GetArray(TEXT("Plugins"), TEXT("ProgramEnabledPlugins"), EnabledPluginNames, GEngineIni); #endif #if !IS_PROGRAM || HACK_HEADER_GENERATOR if (!FParse::Param(FCommandLine::Get(), TEXT("NoEnginePlugins"))) { FProjectManager::Get().GetEnabledPlugins(EnabledPluginNames); } #endif // Build a set from the array TSet< FString > AllEnabledPlugins; AllEnabledPlugins.Append(MoveTemp(EnabledPluginNames)); // Enable all the plugins by name for (const TSharedRef< FPlugin > Plugin : AllPlugins) { if (AllEnabledPlugins.Contains(Plugin->Name)) { Plugin->bEnabled = (!IS_PROGRAM || !bHasProjectFile) || IsPluginSupportedByCurrentTarget(Plugin); if (!Plugin->bEnabled) { AllEnabledPlugins.Remove(Plugin->Name); } } } if (bHasProjectFile) { // Take a copy of the Project's plugins as we may remove some TArray<FPluginReferenceDescriptor> PluginsCopy = Project->Plugins; for(const FPluginReferenceDescriptor& Plugin: PluginsCopy) { if ((Plugin.bEnabled && !FindPluginInstance(Plugin.Name).IsValid()) && (!IS_PROGRAM || AllEnabledPlugins.Contains(Plugin.Name))) // skip if this is a program and the plugin is not enabled { FText Caption(LOCTEXT("PluginMissingCaption", "Plugin missing")); if(Plugin.MarketplaceURL.Len() > 0) { if(FMessageDialog::Open(EAppMsgType::YesNo, FText::Format(LOCTEXT("PluginMissingError", "This project requires the {0} plugin.\n\nWould you like to download it from the the Marketplace?"), FText::FromString(Plugin.Name)), &Caption) == EAppReturnType::Yes) { FString Error; FPlatformProcess::LaunchURL(*Plugin.MarketplaceURL, nullptr, &Error); if(Error.Len() > 0) FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(Error)); return false; } } else { FString Description = (Plugin.Description.Len() > 0) ? FString::Printf(TEXT("\n\n%s"), *Plugin.Description) : FString(); FMessageDialog::Open(EAppMsgType::Ok, FText::Format(LOCTEXT("PluginRequiredError", "This project requires the {0} plugin. {1}"), FText::FromString(Plugin.Name), FText::FromString(Description)), &Caption); if (FMessageDialog::Open(EAppMsgType::YesNo, FText::Format(LOCTEXT("PluginMissingDisable", "Would you like to disable {0}? You will no longer be able to open any assets created using it."), FText::FromString(Plugin.Name)), &Caption) == EAppReturnType::No) { return false; } FText FailReason; if (!IProjectManager::Get().SetPluginEnabled(*Plugin.Name, false, FailReason)) { FMessageDialog::Open(EAppMsgType::Ok, FailReason); } } } } } // If we made it here, we have all the required plugins bHaveAllRequiredPlugins = true; for(const TSharedRef<FPlugin>& Plugin: AllPlugins) { if (Plugin->bEnabled) { // Add the plugin binaries directory const FString PluginBinariesPath = FPaths::Combine(*FPaths::GetPath(Plugin->FileName), TEXT("Binaries"), FPlatformProcess::GetBinariesSubdirectory()); FModuleManager::Get().AddBinariesDirectory(*PluginBinariesPath, Plugin->LoadedFrom == EPluginLoadedFrom::GameProject); #if !IS_MONOLITHIC // Only check this when in a non-monolithic build where modules could be in separate binaries if (Project != NULL && Project->Modules.Num() == 0) { // Content only project - check whether any plugins are incompatible and offer to disable instead of trying to build them later TArray<FString> IncompatibleFiles; if (!FModuleDescriptor::CheckModuleCompatibility(Plugin->Descriptor.Modules, Plugin->LoadedFrom == EPluginLoadedFrom::GameProject, IncompatibleFiles)) { // Ask whether to disable plugin if incompatible FText Caption(LOCTEXT("IncompatiblePluginCaption", "Plugin missing or incompatible")); if (FMessageDialog::Open(EAppMsgType::YesNo, FText::Format(LOCTEXT("IncompatiblePluginText", "Missing or incompatible modules in {0} plugin - would you like to disable it? You will no longer be able to open any assets created using it."), FText::FromString(Plugin->Name)), &Caption) == EAppReturnType::No) { return false; } FText FailReason; if (!IProjectManager::Get().SetPluginEnabled(*Plugin->Name, false, FailReason)) { FMessageDialog::Open(EAppMsgType::Ok, FailReason); } } } #endif //!IS_MONOLITHIC // Build the list of content folders if (Plugin->Descriptor.bCanContainContent) { if (auto EngineConfigFile = GConfig->Find(GEngineIni, false)) { if (auto CoreSystemSection = EngineConfigFile->Find(TEXT("Core.System"))) { CoreSystemSection->AddUnique("Paths", Plugin->GetContentDir()); } } } // Load Default<PluginName>.ini config file if it exists FString PluginConfigDir = FPaths::GetPath(Plugin->FileName) / TEXT("Config/"); FConfigFile PluginConfig; FConfigCacheIni::LoadExternalIniFile(PluginConfig, *Plugin->Name, *FPaths::EngineConfigDir(), *PluginConfigDir, true); if (PluginConfig.Num() > 0) { FString PlaformName = FPlatformProperties::PlatformName(); FString PluginConfigFilename = FString::Printf(TEXT("%s%s/%s.ini"), *FPaths::GeneratedConfigDir(), *PlaformName, *Plugin->Name); FConfigFile& NewConfigFile = GConfig->Add(PluginConfigFilename, FConfigFile()); NewConfigFile.AddMissingProperties(PluginConfig); NewConfigFile.Write(PluginConfigFilename); } } } // Mount all the plugin content folders and pak files TArray<FString> FoundPaks; FPakFileSearchVisitor PakVisitor(FoundPaks); IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); for(TSharedRef<IPlugin> Plugin: GetEnabledPlugins()) { if (Plugin->CanContainContent() && ensure(RegisterMountPointDelegate.IsBound())) { FString ContentDir = Plugin->GetContentDir(); RegisterMountPointDelegate.Execute(Plugin->GetMountedAssetPath(), ContentDir); // Pak files are loaded from <PluginName>/Content/Paks/<PlatformName> if (FPlatformProperties::RequiresCookedData()) { FoundPaks.Reset(); PlatformFile.IterateDirectoryRecursively(*(ContentDir / TEXT("Paks") / FPlatformProperties::PlatformName()), PakVisitor); for (const auto& PakPath : FoundPaks) { if (FCoreDelegates::OnMountPak.IsBound()) { FCoreDelegates::OnMountPak.Execute(PakPath, 0); } } } } } } return bHaveAllRequiredPlugins; }