zoukankan      html  css  js  c++  java
  • ue4 模块的构建和加载

    ue4的代码是模块的形式来组织

    在源码层面,一个包含*.build.cs的目录就是一个模块

    这个目录里的文件在编译后都会被链接在一起,比如一个静态库lib,或者一个动态库dll。

    不管是哪种形式,都需要提供一个给外部操作的接口,也就是一个IModuleInterface指针。

    *注意这里并不是说调用模块内任何函数(或类)都要通过该指针来进行,实际上外部代码只要include了相应的头文件,就能直接调用对应的功能了(比如new一个类,调一个全局函数等),因为实现代码要么做为lib被链接进exe,或是做为dll被动态加载了。

    这个IModuleInterface指针是用来操作做为整体的模块本身的,比如说模块的加载、初始化和卸载,以及访问模块内的一些全局变量(向下转成具体的模块类型后)

    外部获取这个指针,只有一个办法,就是通过FModuleManager上的LoadModule/GetModule。

    而FModuleManager去哪里找需要的模块呢?

    1、在动态链接情况下,根据名字直接找对应的dll就行了,做为合法ue4模块的dll,必定要导出一些约定的函数,来返回自身IModuleInterface指针。

    2、在静态链接时,去一个叫StaticallyLinkedModuleInitializers的Map里找,这就要求所有模块已把自己注册到这个Map里。

    以上2点基本就是FModuleManager::LoadModule的内容。

    而每个模块为满足以上约定,也需要插入一些例程代码,这就是IMPLEMENT_MODULE干的事。

    在静态链接时:

        // 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)
    template< class ModuleClass >
    class FStaticallyLinkedModuleRegistrant
    {
    public:
    
        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
        }
         
        IModuleInterface* InitializeModule( )
        {
            return new ModuleClass();
        }
    };

    FStaticallyLinkedModuleRegistrant是一个注册辅助类,它利用全局变量的构造函数被crt自动调用的特性,实现自动注册逻辑。

    它在构造函数里把一个“注册器”添到前面说的StaticallyLinkedModuleInitializers里,而这个注册器的内容就是创建并返回相应模块。

    在动态链接时:

        #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)

    声明了一个dllexport函数,功能就是创建并返回相应模块。

    实际的应用:

    1、如果模块本身实在没什么特别的,那么就:

    IMPLEMENT_MODULE(FDefaultModuleImpl, ModuleName)

    FDefaultModuleImpl是一个IModuleInterface的空实现,什么都没干,ModuleName则是模块名字,很重要,其它地方要加载模块,就靠这个名字了。

    2、如果模块有自己的初始化逻辑,那么应该实现自己的IModuleInterface子类,比如说MyUtilModule,然后:

    IMPLEMENT_MODULE(MyUtilModule,MyUtil)

    这里类名带Module后缀,而模块名不带,是ue4的惯例。

    3、如果模块是一个包含游戏逻辑的模块(相对于通用功能型模块),那么可以用一个特殊的宏IMPLEMENT_GAME_MODULE,不过目前看来它和前者没啥差别,可能未来有所扩展

    4、更特别的是,如果模块是表示当前游戏项目的“主模块”,那么应该用一个更特殊的宏IMPLEMENT_PRIMARY_GAME_MODULE,而且在构建一个UEBuildGame类型的Target时,必须有一个这样的模块。

    当IS_MONOLITHIC,构建成单个exe时:

    #define IMPLEMENT_PRIMARY_GAME_MODULE( ModuleImplClass, ModuleName, DEPRECATED_GameName ) 
                /* For monolithic builds, we must statically define the game's name string (See Core.h) */ 
                TCHAR GInternalGameName[64] = TEXT( PREPROCESSOR_TO_STRING(UE_PROJECT_NAME) ); 
                /* Implement the GIsGameAgnosticExe variable (See Core.h). */ 
                bool GIsGameAgnosticExe = false; 
                IMPLEMENT_DEBUGGAME() 
                IMPLEMENT_FOREIGN_ENGINE_DIR() 
                IMPLEMENT_GAME_MODULE( ModuleImplClass, ModuleName ) 
                PER_MODULE_BOILERPLATE 
                void UELinkerFixupCheat() 
                { 
                    extern void UELinkerFixups(); 
                    UELinkerFixups(); 
                }

    非单体构建时:

    #define IMPLEMENT_PRIMARY_GAME_MODULE( ModuleImplClass, ModuleName, GameName ) 
            /* Nothing special to do for modular builds.  The game name will be set via the command-line */ 
            IMPLEMENT_GAME_MODULE( ModuleImplClass, ModuleName )

    统一来看,其实也就比普通模块多做了三件事,一是设置游戏名字GInternalGameName,二是设置了GIsGameAgnosticExe=false,三是多了个UELinkerFixupCheat暂且不究。

    GIsGameAgnosticExe是一个有趣的变量,它表示当前这个exe是特定于某游戏的?还是一个通用的加载器?

    如果整体编成一个exe文件,即IS_MONOLITHIC,那肯定是特定于某游戏,这时游戏名GInternalGameName必定是已知固定的,所以以宏参数的形式直接硬编码在exe里了。

    与之相反,当使用模块化构建时(通过给ubt传入-modular参数),将会生成一个exe和一堆dll,并且能加载任何其它以dll形式存的游戏模块,游戏名是未知的,是通过命令行参数来决定要加载哪一个游戏,所以也就用不着GInternalGameName变量了。

    5、当构建一个工具程序(TargetType.Program)时,可以使用专门定制的IMPLEMENT_APPLICATION

    最特别的是它竟然提供了一个FEngineLoop GEngineLoop,而这个变量存在于一般的Game/Editor构建时都会链接的【Launch模块】中。

    不过这也正常,因为在工具程序中一般是要自己写main函数的,所以就用不着重量级的Launch模块了。

  • 相关阅读:
    DVWA 黑客攻防演练(十)反射型 XSS 攻击 Reflected Cross Site Scripting
    DVWA 黑客攻防演练(九) SQL 盲注 SQL Injection (Blind)
    DVWA 黑客攻防演练(八)SQL 注入 SQL Injection
    DVWA 黑客攻防演练(七)Weak Session IDs
    DVWA 黑客攻防演练(六)不安全的验证码 Insecure CAPTCHA
    DVWA 黑客攻防演练(五)文件上传漏洞 File Upload
    工作流表结构设计
    Visual Studio 2019尝鲜----新建空项目体验
    《使用CSLA 2019:CSLA .NET概述》原版和机译文档下载
    .NET快速开发平台的在线预览
  • 原文地址:https://www.cnblogs.com/wellbye/p/5837108.html
Copyright © 2011-2022 走看看