Skip to content

Chapter 2: Plugins and Modules

krogank9 edited this page May 10, 2018 · 15 revisions

When I was learning the Ogre3D engine, the first concept I learned was the plugin. Everything is a plugin in Ogre's world. The author of Ogre abstracted the important interfaces such as file management, rendering engine, and scene manager into plugins. Users can simply re-implement that interface as a new plugin to replace the default plugin. For instance, you can replace the D3D rendering plugin with an OpenGL plugin to replace the renderer Ogre uses, and hot-swap it without recompiling or restarting the application.

NF's plug-in management system is slightly more complicated than OGRE's, mainly because of how each plugin contains modules, which are each registered to the PluginManager and managed by it.

NFPluginLoader is the programs executable entry point. It will automatically find the Plugin.xml file located in the startup directory, and then load each plugin listed in this XML config file.

Let's take a look at an example Plugin.xml file:

<XML>
        <GameServer>
                <Plugin Name="NFKernelPlugin" />
                <Plugin Name="NFConfigPlugin" />
                <Plugin Name="NFGameServerPlugin" />
                <Plugin Name="NFGameServerNet_ServerPlugin" />
                <Plugin Name="NFGameServerNet_ClientPlugin" />
                <Plugin Name="NFLogPlugin" />
            <ConfigPath Name="../" />                
        </GameServer>

        <LoginServer>
                <Plugin Name="NFKernelPlugin" />
                <Plugin Name="NFConfigPlugin" />        
                <Plugin Name="NFLoginLogicPlugin" />
                <Plugin Name="NFLoginNet_ServerPlugin" />
                <Plugin Name="NFLoginNet_ClientPlugin" />
                <Plugin Name="NFLogPlugin" />
            <ConfigPath Name="../" />                
        </LoginServer>

        <MasterServer>
                <Plugin Name="NFKernelPlugin" />
                <Plugin Name="NFConfigPlugin" />
                <Plugin Name="NFMasterServerPlugin" />
                <Plugin Name="NFMasterNet_ServerPlugin" />
                <Plugin Name="NFMasterNet_HttpServerPlugin" />
                <Plugin Name="NFLogPlugin" />
            <ConfigPath Name="../" />                
        </MasterServer>

        <ProxyServer>
                <Plugin Name="NFKernelPlugin" />
                <Plugin Name="NFConfigPlugin" />
                <Plugin Name="NFProxyLogicPlugin" />
                <Plugin Name="NFProxyServerNet_ClientPlugin" />
                <Plugin Name="NFProxyServerNet_ServerPlugin" />
                <Plugin Name="NFLogPlugin" />
            <ConfigPath Name="../" />                                
        </ProxyServer>

        <WorldServer>
                <Plugin Name="NFKernelPlugin" />
                <Plugin Name="NFConfigPlugin" />
                <Plugin Name="NFWorldNet_ClientPlugin" />
                <Plugin Name="NFWorldNet_ServerPlugin" />
                <Plugin Name="NFLogPlugin" />        
            <ConfigPath Name="../" />                
        </WorldServer>
        
        <TutorialServer>
                <Plugin Name="NFKernelPlugin" />
                <Plugin Name="NFConfigPlugin" />
                <Plugin Name="NFLogPlugin" />        
                <Plugin Name="Tutorial1" />        
            <ConfigPath Name="../" />                
        </TutorialServer>
</XML>

Plugin.xml declares each plugin name and the configuration file (NFDataCfg) path for each server to load when started. Because a script is usually used to start the server in batches, an app ID can be passed to the script, for example:

./NFPluginLoader_d -d Server=MasterServer ID=3
./NFPluginLoader_d -d PluginX.xml Server=MasterServer ID=3

The NFCPluginManager singleton class handles all plugin loading. When the application started, the NFCPluginManager singleton will be created and starts loading all plugins listed in the "Plugin.xml" file.

Below is the code:

int main(int argc, char* argv[])
{
    ProcessParameter(argc, argv);

    NFCPluginManager::GetSingletonPtr()->Awake();
    NFCPluginManager::GetSingletonPtr()->Init();
    NFCPluginManager::GetSingletonPtr()->AfterInit();
    NFCPluginManager::GetSingletonPtr()->CheckConfig();
    NFCPluginManager::GetSingletonPtr()->ReadyExecute();

    while (true)
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
        NFCPluginManager::GetSingletonPtr()->Execute();
    }

    NFCPluginManager::GetSingletonPtr()->BeforeShut();
    NFCPluginManager::GetSingletonPtr()->Shut();

    NFCPluginManager::GetSingletonPtr()->ReleaseInstance();

    return 0;
}

The function LoadPluginConfig will be called from in function NFCPluginManager::Awake(). LoadPluginConfig loads and parses the XML file. After that, the NFCPluginManager class will create a plugin instance for each plugin.

How do you add a new plugin and module?

In Linux/Mac, a plugin is a .so file, but on Windows, it's a .dll file. To begin creating a new plugin for NoahGameFrame's world, we will copy the project from Tutorial1, it's the best and easiest way to start creating a new plugin.

We can know from the Tutorial1 that the simplest plugin includes three classes, the one is a class inheritance NFIModule and the other two are the classes that inherit the class NFIplugin.

The codes are below:

class Tutorial1 : public NFIPlugin
{
public:
    Tutorial1(NFIPluginManager* p);

    virtual const int GetPluginVersion();

    virtual const std::string GetPluginName();

    virtual void Install()
    {
        REGISTER_MODULE(pPluginManager, NFIHelloWorld1, NFCHelloWorld1);
    }

    virtual void Uninstall();
};

NFIHelloWorld1: public NFIModule
{

};

class NFCHelloWorld1 : public NFIHelloWorld1
{
public:
    NFCHelloWorld1(NFIPluginManager* p);

    virtual bool Init();
    virtual bool AfterInit();

    virtual bool Execute();

    virtual bool BeforeShut();
    virtual bool Shut();

protected:

};

People who want to create an instance for one module, it's better to use the Macro REGISTER_MODULE to do this. By the way, if we want to use the other module we can find a module by their interface class's name, such as:

NFCHelloWorld1::AfterInit()
{
    m_pKernelModule = pPluginManager->FindModule<NFIKernelModule>();
    m_pHelloWorld1 = pPluginManager->FindModule<NFIHelloWorld1>();
};

Clone this wiki locally