Engine Source Analysis: Module Loading and Startup
发表于更新于阅读次数: 本文字数:5.5k阅读时长 ≈14 分钟
UE是模块化的架构,Engine/Game Project/StandaloneApplication/Plugins都是Module(Unreal Engine API Reference列出了Engine提供的Module列表),本篇文章从FModuleManager的代码来分析一下UE的Module是如何通过FModuleManager::LoadModule加载和启动的。
// If we're linking monolithically we assume all modules are linked in with the main binary. #define IMPLEMENT_MODULE( ModuleImplClass, ModuleName ) \ /** Global registrant object for this module when linked statically */ \ static FStaticallyLinkedModuleRegistrant< ModuleImplClass > ModuleRegistrant##ModuleName( #ModuleName ); \ /** Implement an empty function so that if this module is built as a statically linked lib, */ \ /** static initialization for this lib can be forced by referencing this symbol */ \ void EmptyLinkFunctionForStaticInitialization##ModuleName(){} \ PER_MODULE_BOILERPLATE_ANYLINK(ModuleImplClass, ModuleName)
#else
#define IMPLEMENT_MODULE( ModuleImplClass, ModuleName ) \ \ /**/ \ /* InitializeModule function, called by module manager after this module's DLL has been loaded */ \ /**/ \ /* @return Returns an instance of this module */ \ /**/ \ extern "C" DLLEXPORT IModuleInterface* InitializeModule() \ { \ return new ModuleImplClass(); \ } \ PER_MODULE_BOILERPLATE \ PER_MODULE_BOILERPLATE_ANYLINK(ModuleImplClass, ModuleName)
/** * Explicit constructor that registers a statically linked module */ FStaticallyLinkedModuleRegistrant( const ANSICHAR* InModuleName ) { // Create a delegate to our InitializeModule method FModuleManager::FInitializeStaticallyLinkedModule InitializerDelegate = FModuleManager::FInitializeStaticallyLinkedModule::CreateRaw( this, &FStaticallyLinkedModuleRegistrant<ModuleClass>::InitializeModule );
// Register this module FModuleManager::Get().RegisterStaticallyLinkedModule( FName( InModuleName ), // Module name InitializerDelegate ); // Initializer delegate } /** * Creates and initializes this statically linked module. * * The module manager calls this function through the delegate that was created * in the @see FStaticallyLinkedModuleRegistrant constructor. * * @return A pointer to a new instance of the module. */ IModuleInterface* InitializeModule( ) { returnnewModuleClass(); } };
private: /** Map of module names to a delegate that can initialize each respective statically linked module */ typedef TMap< FName, FInitializeStaticallyLinkedModule > FStaticallyLinkedModuleInitializerMap; FStaticallyLinkedModuleInitializerMap StaticallyLinkedModuleInitializers; // ... };
// Make sure this isn't a module that we had previously loaded, and then unloaded at shutdown time. // // If this assert goes off, your trying to load a module during the shutdown phase that was already // cleaned up. The easiest way to fix this is to change your code to query for an already-loaded // module instead of trying to load it directly. checkf((!ModuleInfo->bWasUnloadedAtShutdown), TEXT("Attempted to load module '%s' that was already unloaded at shutdown. FModuleManager::LoadModule() was called to load a module that was previously loaded, and was unloaded at shutdown time. If this assert goes off, your trying to load a module during the shutdown phase that was already cleaned up. The easiest way to fix this is to change your code to query for an already-loaded module instead of trying to load it directly."), *InModuleName.ToString());
// Check if we're statically linked with the module. Those modules register with the module manager using a static variable, // so hopefully we already know about the name of the module and how to initialize it. const FInitializeStaticallyLinkedModule* ModuleInitializerPtr = StaticallyLinkedModuleInitializers.Find(InModuleName); if (ModuleInitializerPtr != nullptr) { const FInitializeStaticallyLinkedModule& ModuleInitializer(*ModuleInitializerPtr);
// Initialize the module! ModuleInfo->Module = TUniquePtr<IModuleInterface>(ModuleInitializer.Execute());
if (ModuleInfo->Module.IsValid()) { // Startup the module ModuleInfo->Module->StartupModule(); // The module might try to load other dependent modules in StartupModule. In this case, we want those modules shut down AFTER this one because we may still depend on the module at shutdown. ModuleInfo->LoadOrder = FModuleInfo::CurrentLoadOrder++;
// Module was started successfully! Fire callbacks. ModulesChangedEvent.Broadcast(InModuleName, EModuleChangeReason::ModuleLoaded);
// Set the return parameter LoadedModule = ModuleInfo->Module.Get(); } else { UE_LOG(LogModuleManager, Warning, TEXT("ModuleManager: Unable to load module '%s' because InitializeModule function failed (returned nullptr.)"), *InModuleName.ToString()); OutFailureReason = EModuleLoadResult::FailedToInitialize; } } #if IS_MONOLITHIC else { // Monolithic builds that do not have the initializer were *not found* during the build step, so return FileNotFound // (FileNotFound is an acceptable error in some case - ie loading a content only project) UE_LOG(LogModuleManager, Warning, TEXT("ModuleManager: Module '%s' not found - its StaticallyLinkedModuleInitializers function is null."), *InModuleName.ToString()); OutFailureReason = EModuleLoadResult::FileNotFound; } #endif // something.... };
if (bCanUseCache) { // Try to use cache first if (const FString* ModulePathPtr = ModulePathsCache->Find(NamePattern)) { OutModulePaths.Add(FName(NamePattern), *ModulePathPtr); return; }
// Wildcard for all items if (FCString::Strcmp(NamePattern, TEXT("*")) == 0) { OutModulePaths = ModulePathsCache.GetValue(); return; } // Wildcard search if (FCString::Strchr(NamePattern, TEXT('*')) || FCString::Strchr(NamePattern, TEXT('?'))) { bool bFoundItems = false; FString NamePatternString(NamePattern); for (const TPair<FName, FString>& CacheIt : ModulePathsCache.GetValue()) { if (CacheIt.Key.ToString().MatchesWildcard(NamePatternString)) { OutModulePaths.Add(CacheIt.Key, *CacheIt.Value); bFoundItems = true; } }
if (bFoundItems) { return; } } }
// Search through the engine directory FindModulePathsInDirectory(FPlatformProcess::GetModulesDirectory(), false, NamePattern, OutModulePaths);
// Search any engine directories for (int Idx = 0; Idx < EngineBinariesDirectories.Num(); Idx++) { FindModulePathsInDirectory(EngineBinariesDirectories[Idx], false, NamePattern, OutModulePaths); }
// Search any game directories for (int Idx = 0; Idx < GameBinariesDirectories.Num(); Idx++) { FindModulePathsInDirectory(GameBinariesDirectories[Idx], true, NamePattern, OutModulePaths); } }
voidFModuleManager::FindModulePathsInDirectory(const FString& InDirectoryName, bool bIsGameDirectory, const TCHAR* NamePattern, TMap<FName, FString> &OutModulePaths)const { // Figure out the BuildId if it's not already set. if (!BuildId.IsSet()) { FString FileName = FModuleManifest::GetFileName(FPlatformProcess::GetModulesDirectory(), false);
FModuleManifest Manifest; if (!FModuleManifest::TryRead(FileName, Manifest)) { UE_LOG(LogModuleManager, Fatal, TEXT("Unable to read module manifest from '%s'. Module manifests are generated at build time, and must be present to locate modules at runtime."), *FileName) }
BuildId = Manifest.BuildId; }
// Find all the directories to search through, including the base directory TArray<FString> SearchDirectoryNames; IFileManager::Get().FindFilesRecursive(SearchDirectoryNames, *InDirectoryName, TEXT("*"), false, true); SearchDirectoryNames.Insert(InDirectoryName, 0);
// Enumerate the modules in each directory for(const FString& SearchDirectoryName: SearchDirectoryNames) { FModuleManifest Manifest; if (FModuleManifest::TryRead(FModuleManifest::GetFileName(SearchDirectoryName, bIsGameDirectory), Manifest) && Manifest.BuildId == BuildId.GetValue()) { for (const TPair<FString, FString>& Pair : Manifest.ModuleNameToFileName) { if (Pair.Key.MatchesWildcard(NamePattern)) { OutModulePaths.Add(FName(*Pair.Key), *FPaths::Combine(*SearchDirectoryName, *Pair.Value)); } } } } } #endif
// FModuleManager::LoadModuleWithFailureReason (Runtime\Core\Private\Modules\ModuleManager.cpp) IModuleInterface* FModuleManager::LoadModuleWithFailureReason(const FName InModuleName, EModuleLoadResult& OutFailureReason) { // something.... // Make sure that any UObjects that need to be registered were already processed before we go and // load another module. We just do this so that we can easily tell whether UObjects are present // in the module being loaded. if (bCanProcessNewlyLoadedObjects) { ProcessLoadedObjectsCallback.Broadcast(); }
// Determine which file to load for this module. const FString ModuleFileToLoad = FPaths::ConvertRelativePathToFull(ModuleInfo->Filename);
// Clear the handle and set it again below if the module is successfully loaded ModuleInfo->Handle = nullptr;
// Skip this check if file manager has not yet been initialized if (FPaths::FileExists(ModuleFileToLoad)) { ModuleInfo->Handle = FPlatformProcess::GetDllHandle(*ModuleFileToLoad); if (ModuleInfo->Handle != nullptr) { // First things first. If the loaded DLL has UObjects in it, then their generated code's // static initialization will have run during the DLL loading phase, and we'll need to // go in and make sure those new UObject classes are properly registered. { // Sometimes modules are loaded before even the UObject systems are ready. We need to assume // these modules aren't using UObjects. if (bCanProcessNewlyLoadedObjects) { // OK, we've verified that loading the module caused new UObject classes to be // registered, so we'll treat this module as a module with UObjects in it. ProcessLoadedObjectsCallback.Broadcast(); } }
// Find our "InitializeModule" global function, which must exist for all module DLLs FInitializeModuleFunctionPtr InitializeModuleFunctionPtr = (FInitializeModuleFunctionPtr)FPlatformProcess::GetDllExport(ModuleInfo->Handle, TEXT("InitializeModule")); if (InitializeModuleFunctionPtr != nullptr) { if ( ModuleInfo->Module.IsValid() ) { // Assign the already loaded module into the return value, otherwise the return value gives the impression the module failed load! LoadedModule = ModuleInfo->Module.Get(); } else { // Initialize the module! ModuleInfo->Module = TUniquePtr<IModuleInterface>(InitializeModuleFunctionPtr());
if ( ModuleInfo->Module.IsValid() ) { // Startup the module ModuleInfo->Module->StartupModule(); // The module might try to load other dependent modules in StartupModule. In this case, we want those modules shut down AFTER this one because we may still depend on the module at shutdown. ModuleInfo->LoadOrder = FModuleInfo::CurrentLoadOrder++;
// Module was started successfully! Fire callbacks. ModulesChangedEvent.Broadcast(InModuleName, EModuleChangeReason::ModuleLoaded);
// Set the return parameter LoadedModule = ModuleInfo->Module.Get(); } else { UE_LOG(LogModuleManager, Warning, TEXT("ModuleManager: Unable to load module '%s' because InitializeModule function failed (returned nullptr.)"), *ModuleFileToLoad);
FPlatformProcess::FreeDllHandle(ModuleInfo->Handle); ModuleInfo->Handle = nullptr; OutFailureReason = EModuleLoadResult::FailedToInitialize; } } } else { UE_LOG(LogModuleManager, Warning, TEXT("ModuleManager: Unable to load module '%s' because InitializeModule function was not found."), *ModuleFileToLoad);
FPlatformProcess::FreeDllHandle(ModuleInfo->Handle); ModuleInfo->Handle = nullptr; OutFailureReason = EModuleLoadResult::FailedToInitialize; } } else { UE_LOG(LogModuleManager, Warning, TEXT("ModuleManager: Unable to load module '%s' because the file couldn't be loaded by the OS."), *ModuleFileToLoad); OutFailureReason = EModuleLoadResult::CouldNotBeLoadedByOS; } } else { UE_LOG(LogModuleManager, Warning, TEXT("ModuleManager: Unable to load module '%s' because the file '%s' was not found."), *InModuleName.ToString(), *ModuleFileToLoad); OutFailureReason = EModuleLoadResult::FileNotFound; } // something.... }
classIModuleInterface { public: // Note: Even though this is an interface class we need a virtual destructor here because modules are deleted via a pointer to this interface virtual ~IModuleInterface(){}
// Called right after the module DLL has been loaded and the module object has been created // Load dependent modules here, and they will be guaranteed to be available during ShutdownModule. ie: // FModuleManager::Get().LoadModuleChecked(TEXT("HTTP")); virtualvoidStartupModule(){}
// Called before the module has been unloaded virtualvoidPreUnloadCallback(){}
// Called after the module has been reloaded
virtualvoidPostLoadCallback(){}
// Called before the module is unloaded, right before the module object is destroyed. // During normal shutdown, this is called in reverse order that modules finish StartupModule(). // This means that, as long as a module references dependent modules in it's StartupModule(), it // can safely reference those dependencies in ShutdownModule() as well. virtualvoidShutdownModule(){}
// Override this to set whether your module is allowed to be unloaded on the fly // @return Whether the module supports shutdown separate from the rest of the engine. virtualboolSupportsDynamicReloading(){returntrue;}
// Override this to set whether your module would like cleanup on application shutdown // @return Whether the module supports shutdown on application exit virtualboolSupportsAutomaticShutdown(){returntrue;}
// Returns true if this module hosts gameplay code // @return True for "gameplay modules", or false for engine code modules, plugins, etc. virtualboolIsGameModule()const{returnfalse;} };
// LaunchEngineLoop.cpp FEngineLoop::PreInit() { // ... // L1480 // Load Core modules required for everything else to work (needs to be loaded before InitializeRenderingCVarsCaching) if (!LoadCoreModules()) { UE_LOG(LogInit, Error, TEXT("Failed to load Core modules.")); return1; } // ...
// L1572 LoadPreInitModules(); // ...
// L2015 // note: Since 4.20 add ELoadingPhase::PreEarlyLoadingScreen Support. // Load up all modules that need to hook into the loading screen if (!IProjectManager::Get().LoadModulesForProject(ELoadingPhase::PreEarlyLoadingScreen) || !IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::PreEarlyLoadingScreen)) { return1; } // ... // L2202 if ( !LoadStartupCoreModules() ) { // At least one startup module failed to load, return 1 to indicate an error return1; } // ...
// L2211 // Load up all modules that need to hook into the loading screen if (!IProjectManager::Get().LoadModulesForProject(ELoadingPhase::PreLoadingScreen) || !IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::PreLoadingScreen)) { return1; } // ...
// L2310 if ( !LoadStartupModules() ) { // At least one startup module failed to load, return 1 to indicate an error return1; } // ...
// L2457 // Load all the post-engine init modules ensure(IProjectManager::Get().LoadModulesForProject(ELoadingPhase::PostEngineInit)); ensure(IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::PostEngineInit)); // ... // L3044 // Load all the post-engine init modules if (!IProjectManager::Get().LoadModulesForProject(ELoadingPhase::PostEngineInit) || !IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::PostEngineInit)) { GIsRequestingExit = true; return1; } // ...
// LaunchEngineLoop.cpp#L2679 boolFEngineLoop::LoadCoreModules() { // Always attempt to load CoreUObject. It requires additional pre-init which is called from its module's StartupModule method. #if WITH_COREUOBJECT return FModuleManager::Get().LoadModule(TEXT("CoreUObject")) != nullptr; #else returntrue; #endif }
#if !UE_SERVER if (!IsRunningDedicatedServer() ) { if (!GUsingNullRHI) { // This needs to be loaded before InitializeShaderTypes is called FModuleManager::Get().LoadModuleChecked<ISlateRHIRendererModule>("SlateRHIRenderer"); } } #endif
// Initialize ShaderCore before loading or compiling any shaders, // But after Renderer and any other modules which implement shader types. FModuleManager::Get().LoadModule(TEXT("ShaderCore"));
#if WITH_EDITORONLY_DATA // Load the texture compressor module before any textures load. They may // compress asynchronously and that can lead to a race condition. FModuleManager::Get().LoadModule(TEXT("TextureCompressor")); #endif
#endif// WITH_ENGINE
#if (WITH_EDITOR && !(UE_BUILD_SHIPPING || UE_BUILD_TEST)) // Load audio editor module before engine class CDOs are loaded FModuleManager::Get().LoadModule(TEXT("AudioEditor")); FModuleManager::Get().LoadModule(TEXT("AnimationModifiers")); #endif }
// FPlatformApplicationMisc::LoadPreInitModules voidFWindowsPlatformApplicationMisc::LoadPreInitModules() { // D3D11 is not supported on WinXP, so in this case we use the OpenGL RHI if(FWindowsPlatformMisc::VerifyWindowsVersion(6, 0)) { //#todo-rco: Only try on Win10 constbool bForceD3D12 = FParse::Param(FCommandLine::Get(), TEXT("d3d12")) || FParse::Param(FCommandLine::Get(), TEXT("dx12")); if (bForceD3D12) { FModuleManager::Get().LoadModule(TEXT("D3D12RHI")); } FModuleManager::Get().LoadModule(TEXT("D3D11RHI")); } FModuleManager::Get().LoadModule(TEXT("OpenGLDrv")); }
// initialize messaging SlowTask.EnterProgressFrame(10); if (FPlatformProcess::SupportsMultithreading()) { FModuleManager::LoadModuleChecked<IMessagingModule>("Messaging"); }
// Init Scene Reconstruction support #if !UE_SERVER if (!IsRunningDedicatedServer()) { FModuleManager::LoadModuleChecked<IMRMeshModule>("MRMesh"); } #endif
#if !UE_BUILD_SHIPPING // Need to load up the SlateReflector module to initialize the WidgetSnapshotService FModuleManager::Get().LoadModule("SlateReflector"); #endif// !UE_BUILD_SHIPPING }
#if WITH_EDITOR // In dedicated server builds with the editor, we need to load UMG/UMGEditor for compiling blueprints. // UMG must be loaded for runtime and cooking. FModuleManager::Get().LoadModule("UMG"); #else if ( !IsRunningDedicatedServer() ) { // UMG must be loaded for runtime and cooking. FModuleManager::Get().LoadModule("UMG"); } #endif//WITH_EDITOR
// Load all Development modules SlowTask.EnterProgressFrame(20); if (!IsRunningDedicatedServer()) { #if WITH_UNREAL_DEVELOPER_TOOLS FModuleManager::Get().LoadModule("MessageLog"); FModuleManager::Get().LoadModule("CollisionAnalyzer"); #endif//WITH_UNREAL_DEVELOPER_TOOLS }
SlowTask.EnterProgressFrame(30); #if (WITH_EDITOR && !(UE_BUILD_SHIPPING || UE_BUILD_TEST)) // HACK: load BT editor as early as possible for statically initialized assets (non cooked BT assets needs it) // cooking needs this module too FModuleManager::Get().LoadModule(TEXT("BehaviorTreeEditor"));
// Ability tasks are based on GameplayTasks, so we need to make sure that module is loaded as well FModuleManager::Get().LoadModule(TEXT("GameplayTasksEditor"));
// Load the StringTableEditor module to register its asset actions FModuleManager::Get().LoadModule("StringTableEditor");
if( !IsRunningDedicatedServer() ) { // VREditor needs to be loaded in non-server editor builds early, so engine content Blueprints can be loaded during DDC generation FModuleManager::Get().LoadModule(TEXT("VREditor")); } // -----------------------------------------------------
// HACK: load EQS editor as early as possible for statically initialized assets (non cooked EQS assets needs it) // cooking needs this module too bool bEnvironmentQueryEditor = false; GConfig->GetBool(TEXT("EnvironmentQueryEd"), TEXT("EnableEnvironmentQueryEd"), bEnvironmentQueryEditor, GEngineIni); if (bEnvironmentQueryEditor #if WITH_EDITOR || GetDefault<UEditorExperimentalSettings>()->bEQSEditor #endif// WITH_EDITOR ) { FModuleManager::Get().LoadModule(TEXT("EnvironmentQueryEditor")); }
// We need this for blueprint projects that have online functionality. //FModuleManager::Get().LoadModule(TEXT("OnlineBlueprintSupport"));
if (IsRunningCommandlet()) { FModuleManager::Get().LoadModule(TEXT("IntroTutorials")); FModuleManager::Get().LoadModule(TEXT("Blutility")); }
SlowTask.EnterProgressFrame(1); // Load any modules that want to be loaded before default modules are loaded up. if (!IProjectManager::Get().LoadModulesForProject(ELoadingPhase::PreDefault) || !IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::PreDefault)) { returnfalse; }
SlowTask.EnterProgressFrame(1); // Load modules that are configured to load in the default phase if (!IProjectManager::Get().LoadModulesForProject(ELoadingPhase::Default) || !IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::Default)) { returnfalse; }
SlowTask.EnterProgressFrame(1); // Load any modules that want to be loaded after default modules are loaded up. if (!IProjectManager::Get().LoadModulesForProject(ELoadingPhase::PostDefault) || !IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::PostDefault)) { returnfalse; }
Sets the type of Module. Valid options are Runtime, RuntimeNoCommandlet, Developer, Editor, EditorNoCommandlet, and Program. This type determines which types of applications this Plugin’s Module is suitable for loading in. For example, some plugins may include modules that are only designed to be loaded when the editor is running. Runtime modules will be loaded in all cases, even in shipped games. Developer modules will only be loaded in development runtime or editor builds, but never in shipping builds. Editor modules will only be loaded when the editor is starting up. Your Plugin can use a combination of modules of different types.
namespace EHostType { enumType { // Any target using the UE4 runtime Runtime, // Any target except for commandlet RuntimeNoCommandlet, // Any target or program RuntimeAndProgram, // Loaded only in cooked builds CookedOnly, // Loaded only when the engine has support for developer tools enabled Developer, // Loaded only by the editor Editor, // Loaded only by the editor, except when running commandlets EditorNoCommandlet, // Loaded only by programs Program, // Loaded only by servers ServerOnly, // Loaded only by clients ClientOnly, // NOTE: If you add a new value, make sure to update the ToString() method below!
Max }; // ... }
Plugin:LoadingPhase(Optional)
.uplugin Module LoadingPhase Descriptors.
If specified, controls when the plugin is loaded at start-up. This is an advanced option that should not normally be required. The valid options are Default(which is used when no LoadingPhase is specified), PreDefault, and PostConfigInit. PostConfigInit enables the module to be loaded before the engine has finished starting up key subsystems. PreDefault loads just before the normal phase. Typically, this is only needed if you expect game modules to depend directly on content within your plugin, or types declared within the plugin’s code.
namespace ELoadingPhase { enumType { /** Loaded before the engine is fully initialized, immediately after the config system has been initialized. Necessary only for very low-level hooks */ PostConfigInit,
/** Loaded before the engine is fully initialized for modules that need to hook into the loading screen before it triggers */ PreLoadingScreen,
/** Right before the default phase */ PreDefault,
/** Loaded at the default loading point during startup (during engine init, after game modules are loaded.) */ Default,
/** Right after the default phase */ PostDefault,
/** After the engine has been initialized */ PostEngineInit,
/** Do not automatically load this module */ None,
// NOTE: If you add a new value, make sure to update the ToString() method below! Max }; }
if ( FailureReason == EModuleLoadResult::FileNotFound ) { FailureMessage = FText::Format( LOCTEXT("PrimaryGameModuleNotFound", "The game module '{0}' could not be found. Please ensure that this module exists and that it is compiled."), TextModuleName ); } elseif ( FailureReason == EModuleLoadResult::FileIncompatible ) { FailureMessage = FText::Format( LOCTEXT("PrimaryGameModuleIncompatible", "The game module '{0}' does not appear to be up to date. This may happen after updating the engine. Please recompile this module and try again."), TextModuleName ); } elseif ( FailureReason == EModuleLoadResult::FailedToInitialize ) { FailureMessage = FText::Format( LOCTEXT("PrimaryGameModuleFailedToInitialize", "The game module '{0}' could not be successfully initialized after it was loaded."), TextModuleName ); } elseif ( FailureReason == EModuleLoadResult::CouldNotBeLoadedByOS ) { FailureMessage = FText::Format( LOCTEXT("PrimaryGameModuleCouldntBeLoaded", "The game module '{0}' could not be loaded. There may be an operating system error or the module may not be properly set up."), TextModuleName ); } else { ensure(0); // If this goes off, the error handling code should be updated for the new enum values! FailureMessage = FText::Format( LOCTEXT("PrimaryGameModuleGenericLoadFailure", "The game module '{0}' failed to load for an unspecified reason. Please report this error."), TextModuleName ); }
// Runtime\Project\Private\ModuleDescriptor.cpp // LoadingPhase - is want load Module LoadingPhase Descriptor // Modules - is current project all modoules(CurrentProject->Modules) // ModuleLoadErrors - is module-moduleLoadError mapping voidFModuleDescriptor::LoadModulesForPhase(ELoadingPhase::Type LoadingPhase, const TArray<FModuleDescriptor>& Modules, TMap<FName, EModuleLoadResult>& ModuleLoadErrors) { FScopedSlowTask SlowTask(Modules.Num()); for (int Idx = 0; Idx < Modules.Num(); Idx++) { SlowTask.EnterProgressFrame(1); const FModuleDescriptor& Descriptor = Modules[Idx];
// Don't need to do anything if this module is already loaded if (!FModuleManager::Get().IsModuleLoaded(Descriptor.Name)) { if (LoadingPhase == Descriptor.LoadingPhase && Descriptor.IsLoadedInCurrentConfiguration()) { // @todo plugin: DLL search problems. Plugins that statically depend on other modules within this plugin may not be found? Need to test this.
// NOTE: Loading this module may cause other modules to become loaded, both in the engine or game, or other modules // that are part of this project or plugin. That's totally fine. EModuleLoadResult FailureReason; IModuleInterface* ModuleInterface = FModuleManager::Get().LoadModuleWithFailureReason(Descriptor.Name, FailureReason); if (ModuleInterface == nullptr) { // The module failed to load. Note this in the ModuleLoadErrors list. ModuleLoadErrors.Add(Descriptor.Name, FailureReason); } } } } }
boolFModuleDescriptor::IsLoadedInCurrentConfiguration()const { // Check that the module is built for this configuration if(!IsCompiledInCurrentConfiguration()) { returnfalse; }
// Check that the runtime environment allows it to be loaded switch (Type) { case EHostType::RuntimeAndProgram: #if (WITH_ENGINE || WITH_PLUGIN_SUPPORT) returntrue; #endif break;