Files
UnrealEngineUWP/Engine/Source/Runtime/Projects/Private/PluginManager.cpp
Matthew Griffin 755f725131 Merging //UE4/Release-4.11 to //UE4/Main (up to CL#2852902)
==========================
MAJOR FEATURES + CHANGES
==========================

Change 2835191 on 2016/01/19 by Nick.Whiting

	Invert the y-axis on the SteamVR controllers to match the convention of the engine and the rest of the gamepads

	#jira UE-22705

Change 2835686 on 2016/01/20 by Gareth.Martin

	Fixed landscape material instances not being updated if holes are painted on a landscape that doesn't have the landscape visibility mask node in the material and then the visibility mask node is added to the material later.
	#jira UE-18187

Change 2835767 on 2016/01/20 by Richard.Hinckley

	#jira UE-25499 Added a cursor to TopDown template (C++ version) to match the BP version.

Change 2835772 on 2016/01/20 by Richard.Hinckley

	#jira UE-25499 Adding the material asset for the C++ TopDown template's cursor.

Change 2835811 on 2016/01/20 by Taizyd.Korambayil

	#jira UE-25699 Added Validity Checks in BP logic, unchecked CDO for Pixel Ship, to Fix Log Warnings
	#jira UE-25704 Adjusted Matinee to happen at Box Location
	#jira UE-25688 Adjusted Player Starts
	#jira UE-25693 Adjusted Player Starts

Change 2835863 on 2016/01/20 by Gareth.Martin

	Fixed crash in the landscape ramp and mirror tools if the streaming level containing the landscape is hidden (or possibly if the landscape actor is deleted)
	#jira UE-24883

Change 2835889 on 2016/01/20 by Taizyd.Korambayil

	#jira UE-25698 Enabled V-sync, also fixed up player Respawn Issue

Change 2835995 on 2016/01/20 by Jamie.Dale

	The output log now hard-wraps lines to prevent long lines causing performance issues

	#jira UE-24187

Change 2836052 on 2016/01/20 by Taizyd.Korambayil

	#jira UE-25675 Added Blocking Volume to prevent Player from Falling off map
	#jira UE-25676 Added Blocking Volumes so that the Player doesn't get stucl at awkward corners under the Bridge

Change 2836137 on 2016/01/20 by Chad.Taylor

	Vehicle and VehicleAdv template content fixes for new VR camera

	#jira UE-25507

Change 2836166 on 2016/01/20 by Gareth.Martin

	Fixed hiding a streaming level containing a landscape causing the landscape editor to switch to the "New Landscape" tool instead of exiting
	#jira UE-25093

Change 2836174 on 2016/01/20 by Chad.Taylor

	IHeadMountedDisplay crash fix associated with accessing a dangling pointer.

	#jira UE-25272

Change 2836179 on 2016/01/20 by Jamie.Dale

	Optimized FShapedGlyphSequence reverse look-up

	There's now a reverse look-up map of cluster indices to their glyph data in order to avoid brute force looping

	#jira UE-24187

Change 2836286 on 2016/01/20 by Chris.Babcock

	Update Qualcomm TextureConverter for OSX
	#jira UE-22092
	#ue4
	#android

Change 2836328 on 2016/01/20 by Nick.Darnell

	Fixing a problem with widget components crashing on destruction with the render commands to pre/post render for window render commands needing access to the policy, but it potentially being deleted.  Inserting a NoOp command that keeps the shared ptr alive through the RHI render process.

	#jira UE-25752

Change 2836342 on 2016/01/20 by Nick.Darnell

	Depending on shutdown order, the Slate Renderer may go away, and then render data handles may not be collected correctly because they are trying to reference a pointer that's no longer valid and cause a crash on exit. The correct approach would be to have render handles actually have a pointer back to who owns them, in this case the RHI Resource Manager, which is still alive and well at this point in the pipeline.  Then if the resource manager is collected, it forces all handles to get cleaned up correctly, or if the handles are collected first, they can be sure they've got a valid pointer back to the resource manager.

	#jira UE-25753

Change 2836358 on 2016/01/20 by Taizyd.Korambayil

	#jira UE-25710 Replaced Deprecated Nodes

Change 2836510 on 2016/01/20 by Taizyd.Korambayil

	#jira UE-25718 Adjsuted BP to make pointer decal rotate in the direction of surface

Change 2836564 on 2016/01/20 by Taizyd.Korambayil

	#jira UE-25716 Added bool to store last Moved Direction

Change 2836697 on 2016/01/20 by Taizyd.Korambayil

	#jira UE-25740 Removed unused VR Nodes to remove Log errors on Mac

Change 2836725 on 2016/01/20 by Peter.Sauerbrei

	workaround for thread race when trying to release the TargetDeviceService endpoint after an unclaim message is sent
	#jira UE-25123

Change 2836782 on 2016/01/20 by Jamie.Dale

	Added FTextLayout::AddLines

	This is similar to AddLine, however it allows you to add multiple lines in a single call, thus avoiding the re-justification cost associated with each call to AddLine.

	AddLine has also been changed to take the same structure type as AddLines (which takes an array of these structures), and the existing version of AddLine has been deprecated.

	#jira UE-24187

Change 2836801 on 2016/01/20 by Jeff.Campeau

[CL 2857187 by Matthew Griffin in Main branch]
2016-02-05 11:54:00 -05:00

693 lines
21 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "ProjectsPrivatePCH.h"
DEFINE_LOG_CATEGORY_STATIC( LogPluginManager, Log, All );
#define LOCTEXT_NAMESPACE "PluginManager"
namespace PluginSystemDefs
{
/** File extension of plugin descriptor files.
NOTE: This constant exists in UnrealBuildTool code as well. */
static const TCHAR PluginDescriptorFileExtension[] = TEXT( ".uplugin" );
/**
* Parsing the command line and loads any foreign plugins that were
* specified using the -PLUGIN= command.
*
* @param CommandLine The commandline used to launch the editor.
* @param SearchPathsOut
* @return The number of plugins that were specified using the -PLUGIN param.
*/
static int32 GetAdditionalPluginPaths(TSet<FString>& PluginPathsOut)
{
const TCHAR* SwitchStr = TEXT("PLUGIN=");
const int32 SwitchLen = FCString::Strlen(SwitchStr);
int32 PluginCount = 0;
const TCHAR* SearchStr = FCommandLine::Get();
do
{
FString PluginPath;
SearchStr = FCString::Strfind(SearchStr, SwitchStr);
if (FParse::Value(SearchStr, SwitchStr, PluginPath))
{
FString PluginDir = FPaths::GetPath(PluginPath);
PluginPathsOut.Add(PluginDir);
++PluginCount;
SearchStr += SwitchLen + PluginPath.Len();
}
else
{
break;
}
} while (SearchStr != nullptr);
#if IS_PROGRAM
// For programs that have the project dir set, look for plugins under the project directory
const FProjectDescriptor *Project = IProjectManager::Get().GetCurrentProject();
if (Project != nullptr)
{
PluginPathsOut.Add(FPaths::GetPath(FPaths::GetProjectFilePath()) / TEXT("Plugins"));
}
#endif
return PluginCount;
}
}
FPlugin::FPlugin(const FString& InFileName, const FPluginDescriptor& InDescriptor, EPluginLoadedFrom InLoadedFrom)
: Name(FPaths::GetBaseFilename(InFileName))
, FileName(InFileName)
, Descriptor(InDescriptor)
, LoadedFrom(InLoadedFrom)
, bEnabled(false)
{
}
FPlugin::~FPlugin()
{
}
FString FPlugin::GetName() const
{
return Name;
}
FString FPlugin::GetDescriptorFileName() const
{
return FileName;
}
FString FPlugin::GetBaseDir() const
{
return FPaths::GetPath(FileName);
}
FString FPlugin::GetContentDir() const
{
return FPaths::GetPath(FileName) / TEXT("Content");
}
FString FPlugin::GetMountedAssetPath() const
{
return FString::Printf(TEXT("/%s/"), *Name);
}
bool FPlugin::IsEnabled() const
{
return bEnabled;
}
bool FPlugin::CanContainContent() const
{
return Descriptor.bCanContainContent;
}
EPluginLoadedFrom FPlugin::GetLoadedFrom() const
{
return LoadedFrom;
}
const FPluginDescriptor& FPlugin::GetDescriptor() const
{
return Descriptor;
}
bool FPlugin::UpdateDescriptor(const FPluginDescriptor& NewDescriptor, FText& OutFailReason)
{
if(!NewDescriptor.Save(FileName, OutFailReason))
{
return false;
}
Descriptor = NewDescriptor;
return true;
}
FPluginManager::FPluginManager()
: bHaveConfiguredEnabledPlugins(false)
, bHaveAllRequiredPlugins(false)
{
DiscoverAllPlugins();
}
FPluginManager::~FPluginManager()
{
// NOTE: All plugins and modules should be cleaned up or abandoned by this point
// @todo plugin: Really, we should "reboot" module manager's unloading code so that it remembers at which startup phase
// modules were loaded in, so that we can shut groups of modules down (in reverse-load order) at the various counterpart
// shutdown phases. This will fix issues where modules that are loaded after game modules are shutdown AFTER many engine
// systems are already killed (like GConfig.) Currently the only workaround is to listen to global exit events, or to
// explicitly unload your module somewhere. We should be able to handle most cases automatically though!
}
void FPluginManager::RefreshPluginsList()
{
// Read a new list of all plugins
TArray<TSharedRef<FPlugin>> NewPlugins;
ReadAllPlugins(NewPlugins, PluginDiscoveryPaths);
// Build a list of filenames for plugins which are enabled, and remove the rest
TArray<FString> EnabledPluginFileNames;
for(int32 Idx = 0; Idx < AllPlugins.Num(); Idx++)
{
const TSharedRef<FPlugin>& Plugin = AllPlugins[Idx];
if(Plugin->bEnabled)
{
EnabledPluginFileNames.Add(Plugin->FileName);
}
else
{
AllPlugins.RemoveAt(Idx--);
}
}
// Add all the plugins which aren't already enabled
for(TSharedRef<FPlugin>& NewPlugin: NewPlugins)
{
if(!EnabledPluginFileNames.Contains(NewPlugin->FileName))
{
AllPlugins.Add(NewPlugin);
}
}
}
void FPluginManager::DiscoverAllPlugins()
{
ensure( AllPlugins.Num() == 0 ); // Should not have already been initialized!
PluginSystemDefs::GetAdditionalPluginPaths(PluginDiscoveryPaths);
ReadAllPlugins(AllPlugins, PluginDiscoveryPaths);
}
void FPluginManager::ReadAllPlugins(TArray<TSharedRef<FPlugin>>& Plugins, const TSet<FString>& ExtraSearchPaths)
{
#if (WITH_ENGINE && !IS_PROGRAM) || WITH_PLUGIN_SUPPORT
// Find "built-in" plugins. That is, plugins situated right within the Engine directory.
ReadPluginsInDirectory(FPaths::EnginePluginsDir(), EPluginLoadedFrom::Engine, Plugins);
// Find plugins in the game project directory (<MyGameProject>/Plugins)
if( FApp::HasGameName() )
{
ReadPluginsInDirectory(FPaths::GamePluginsDir(), EPluginLoadedFrom::GameProject, Plugins);
}
for (const FString& ExtraSearchPath : ExtraSearchPaths)
{
ReadPluginsInDirectory(ExtraSearchPath, EPluginLoadedFrom::GameProject, Plugins);
}
#endif
}
void FPluginManager::ReadPluginsInDirectory(const FString& PluginsDirectory, const EPluginLoadedFrom LoadedFrom, TArray<TSharedRef<FPlugin>>& Plugins)
{
// Make sure the directory even exists
if(FPlatformFileManager::Get().GetPlatformFile().DirectoryExists(*PluginsDirectory))
{
TArray<FString> FileNames;
FindPluginsInDirectory(PluginsDirectory, FileNames);
for(const FString& FileName: FileNames)
{
FPluginDescriptor Descriptor;
FText FailureReason;
if(Descriptor.Load(FileName, FailureReason))
{
Plugins.Add(MakeShareable(new FPlugin(FileName, Descriptor, LoadedFrom)));
}
else
{
// NOTE: Even though loading of this plugin failed, we'll keep processing other plugins
FString FullPath = FPaths::ConvertRelativePathToFull(FileName);
FText FailureMessage = FText::Format(LOCTEXT("FailureFormat", "{0} ({1})"), FailureReason, FText::FromString(FullPath));
FText DialogTitle = LOCTEXT("PluginFailureTitle", "Failed to load Plugin");
UE_LOG(LogPluginManager, Error, TEXT("%s"), *FailureMessage.ToString());
FMessageDialog::Open(EAppMsgType::Ok, FailureMessage, &DialogTitle);
}
}
}
}
void FPluginManager::FindPluginsInDirectory(const FString& PluginsDirectory, TArray<FString>& FileNames)
{
// Class to enumerate the contents of a directory, and find all sub-directories and plugin descriptors within it
struct FPluginDirectoryVisitor : public IPlatformFile::FDirectoryVisitor
{
TArray<FString> SubDirectories;
TArray<FString> PluginDescriptors;
virtual bool Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory) override
{
FString FilenameOrDirectoryStr = FilenameOrDirectory;
if(bIsDirectory)
{
SubDirectories.Add(FilenameOrDirectoryStr);
}
else if(FilenameOrDirectoryStr.EndsWith(TEXT(".uplugin")))
{
PluginDescriptors.Add(FilenameOrDirectoryStr);
}
return true;
}
};
// Enumerate the contents of the current directory
FPluginDirectoryVisitor Visitor;
FPlatformFileManager::Get().GetPlatformFile().IterateDirectory(*PluginsDirectory, Visitor);
// If there's no plugins in this directory, recurse through all the child directories
if(Visitor.PluginDescriptors.Num() == 0)
{
for(const FString& SubDirectory: Visitor.SubDirectories)
{
FindPluginsInDirectory(SubDirectory, FileNames);
}
}
else
{
for(const FString& PluginDescriptor: Visitor.PluginDescriptors)
{
FileNames.Add(PluginDescriptor);
}
}
}
// Helper class to find all pak files.
class FPakFileSearchVisitor : public IPlatformFile::FDirectoryVisitor
{
TArray<FString>& FoundFiles;
public:
FPakFileSearchVisitor(TArray<FString>& InFoundFiles)
: FoundFiles(InFoundFiles)
{}
virtual bool Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory)
{
if (bIsDirectory == false)
{
FString Filename(FilenameOrDirectory);
if (Filename.MatchesWildcard(TEXT("*.pak")))
{
FoundFiles.Add(Filename);
}
}
return true;
}
};
bool FPluginManager::IsPluginSupportedByCurrentTarget(TSharedRef<FPlugin> Plugin) const
{
bool bSupported = false;
if (Plugin->GetDescriptor().Modules.Num())
{
for (const FModuleDescriptor& Module : Plugin->GetDescriptor().Modules)
{
// Programs support only program type plugins
// Non-program targets don't support from plugins
#if IS_PROGRAM
if (Module.Type == EHostType::Program)
{
bSupported = true;
}
#else
if (Module.Type != EHostType::Program)
{
bSupported = true;
}
#endif
}
}
else
{
bSupported = true;
}
return bSupported;
}
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 (!FParse::Param(FCommandLine::Get(), TEXT("NoEnginePlugins")))
{
FProjectManager::Get().GetEnabledPlugins(EnabledPluginNames);
}
// 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() && !Plugin.bOptional) &&
(!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, nullptr);
}
}
}
}
}
}
return bHaveAllRequiredPlugins;
}
TSharedPtr<FPlugin> FPluginManager::FindPluginInstance(const FString& Name)
{
TSharedPtr<FPlugin> Result;
for(const TSharedRef<FPlugin>& Instance : AllPlugins)
{
if(Instance->Name == Name)
{
Result = Instance;
break;
}
}
return Result;
}
bool FPluginManager::LoadModulesForEnabledPlugins( const ELoadingPhase::Type LoadingPhase )
{
// Figure out which plugins are enabled
if(!ConfigureEnabledPlugins())
{
return false;
}
FScopedSlowTask SlowTask(AllPlugins.Num());
// Load plugins!
for( const TSharedRef< FPlugin > Plugin : AllPlugins )
{
SlowTask.EnterProgressFrame(1);
if ( Plugin->bEnabled )
{
TMap<FName, EModuleLoadResult> ModuleLoadFailures;
FModuleDescriptor::LoadModulesForPhase(LoadingPhase, Plugin->Descriptor.Modules, ModuleLoadFailures);
FText FailureMessage;
for( auto FailureIt( ModuleLoadFailures.CreateConstIterator() ); FailureIt; ++FailureIt )
{
const auto ModuleNameThatFailedToLoad = FailureIt.Key();
const auto FailureReason = FailureIt.Value();
if( FailureReason != EModuleLoadResult::Success )
{
const FText PluginNameText = FText::FromString(Plugin->Name);
const FText TextModuleName = FText::FromName(FailureIt.Key());
if ( FailureReason == EModuleLoadResult::FileNotFound )
{
FailureMessage = FText::Format( LOCTEXT("PluginModuleNotFound", "Plugin '{0}' failed to load because module '{1}' could not be found. Please ensure the plugin is properly installed, otherwise consider disabling the plugin for this project."), PluginNameText, TextModuleName );
}
else if ( FailureReason == EModuleLoadResult::FileIncompatible )
{
FailureMessage = FText::Format( LOCTEXT("PluginModuleIncompatible", "Plugin '{0}' failed to load because module '{1}' does not appear to be compatible with the current version of the engine. The plugin may need to be recompiled."), PluginNameText, TextModuleName );
}
else if ( FailureReason == EModuleLoadResult::CouldNotBeLoadedByOS )
{
FailureMessage = FText::Format( LOCTEXT("PluginModuleCouldntBeLoaded", "Plugin '{0}' failed to load because module '{1}' could not be loaded. There may be an operating system error or the module may not be properly set up."), PluginNameText, TextModuleName );
}
else if ( FailureReason == EModuleLoadResult::FailedToInitialize )
{
FailureMessage = FText::Format( LOCTEXT("PluginModuleFailedToInitialize", "Plugin '{0}' failed to load because module '{1}' could be initialized successfully after it was loaded."), PluginNameText, TextModuleName );
}
else
{
ensure(0); // If this goes off, the error handling code should be updated for the new enum values!
FailureMessage = FText::Format( LOCTEXT("PluginGenericLoadFailure", "Plugin '{0}' failed to load because module '{1}' could not be loaded for an unspecified reason. This plugin's functionality will not be available. Please report this error."), PluginNameText, TextModuleName );
}
// Don't need to display more than one module load error per plugin that failed to load
break;
}
}
if( !FailureMessage.IsEmpty() )
{
FMessageDialog::Open(EAppMsgType::Ok, FailureMessage);
return false;
}
}
}
return true;
}
void FPluginManager::SetRegisterMountPointDelegate( const FRegisterMountPointDelegate& Delegate )
{
RegisterMountPointDelegate = Delegate;
}
bool FPluginManager::AreRequiredPluginsAvailable()
{
return ConfigureEnabledPlugins();
}
bool FPluginManager::CheckModuleCompatibility(TArray<FString>& OutIncompatibleModules)
{
if(!ConfigureEnabledPlugins())
{
return false;
}
bool bResult = true;
for (TArray< TSharedRef< FPlugin > >::TConstIterator Iter(AllPlugins); Iter; ++Iter)
{
const TSharedRef< FPlugin > &Plugin = *Iter;
if (Plugin->bEnabled && !FModuleDescriptor::CheckModuleCompatibility(Plugin->Descriptor.Modules, Plugin->LoadedFrom == EPluginLoadedFrom::GameProject, OutIncompatibleModules))
{
bResult = false;
}
}
return bResult;
}
IPluginManager& IPluginManager::Get()
{
// Single instance of manager, allocated on demand and destroyed on program exit.
static FPluginManager* PluginManager = NULL;
if( PluginManager == NULL )
{
PluginManager = new FPluginManager();
}
return *PluginManager;
}
TSharedPtr<IPlugin> FPluginManager::FindPlugin(const FString& Name)
{
TSharedPtr<IPlugin> Plugin;
for(TSharedRef<FPlugin>& PossiblePlugin : AllPlugins)
{
if(PossiblePlugin->Name == Name)
{
Plugin = PossiblePlugin;
break;
}
}
return Plugin;
}
TArray<TSharedRef<IPlugin>> FPluginManager::GetEnabledPlugins()
{
TArray<TSharedRef<IPlugin>> Plugins;
for(TSharedRef<FPlugin>& PossiblePlugin : AllPlugins)
{
if(PossiblePlugin->bEnabled)
{
Plugins.Add(PossiblePlugin);
}
}
return Plugins;
}
TArray<TSharedRef<IPlugin>> FPluginManager::GetDiscoveredPlugins()
{
TArray<TSharedRef<IPlugin>> Plugins;
for(TSharedRef<FPlugin>& Plugin : AllPlugins)
{
Plugins.Add(Plugin);
}
return Plugins;
}
TArray< FPluginStatus > FPluginManager::QueryStatusForAllPlugins() const
{
TArray< FPluginStatus > PluginStatuses;
for( auto PluginIt( AllPlugins.CreateConstIterator() ); PluginIt; ++PluginIt )
{
const TSharedRef< FPlugin >& Plugin = *PluginIt;
FPluginStatus PluginStatus;
PluginStatus.Name = Plugin->Name;
PluginStatus.PluginDirectory = FPaths::GetPath(Plugin->FileName);
PluginStatus.bIsEnabled = Plugin->bEnabled;
PluginStatus.Descriptor = Plugin->Descriptor;
PluginStatus.LoadedFrom = Plugin->LoadedFrom;
PluginStatuses.Add( PluginStatus );
}
return PluginStatuses;
}
void FPluginManager::AddPluginSearchPath(const FString& ExtraDiscoveryPath, bool bRefresh)
{
PluginDiscoveryPaths.Add(ExtraDiscoveryPath);
if (bRefresh)
{
RefreshPluginsList();
}
}
#undef LOCTEXT_NAMESPACE