Utilizator:
Parola:
Am uitat parola... | Cont nou!


Articole Resurse Echipe Competiții Proiecte Forum DevBlogs Locuri de muncă GDROMag Issue#1 GDROCon 2007

 
Forum » Articole » Programare » Plugin System Design (C++)

Articol:
Plugin System Design (C++)
Autor: nekitu, postat pe 14 May 2007 12:29:56
Versiune printabilã
Introducere

Am observat ca nu prea sunt articole despre cum sa faci un sistem de plugin-uri simplu, eficient si usor de adaugat in orice aplicatie C++ (in acest articol), asa ca am sa incerc sa va prezint modelul la care m-am stabilit eu dupa multe alte modele mai mult sau mai putin reusite. Voi fi scurt si la obiect, nu-mi place sa vorbesc / scriu prea mult Smile.

Arhitectura sistemului de plugin-uri

Este formata din cateva clase: PluginClassInfo, PluginInfo, Plugin si PluginManager ( singleton anyone? Very Happy, no it's not )
Plugin-urile sunt defapt DLL-uri care sunt incarcate de PluginManager, acesta cauta o functie speciala in acel DLL, de exemplu:

Cod sursă:
PluginInfo* __declspec(dllexport) __getPluginInfo() { return (PluginInfo*) &g_thisPluginInfo; }


unde g_thisPluginInfo este o clasa derivata din PluginInfo, care specifica info despre acel plugin.
Fiecare plugin DLL poate contine mai multe clase-plugin, sa luam exemplu un plugin dll numit extendedEntities.dll, in care ai mai multe clase-plugin: MonsterEntity, VehicleEntity, SkyboxEntity ..etc

Clasa interfata PluginClassInfo, care tine informatii despre o clasa-plugin, sa ii zicem un fel de plugin-class-descriptor ( surse din motorul meu Nytro ):

Cod sursă:

//! This class holds the info about a C++ class from inside a plugin
class NYTRO_API PluginClassInfo
{
    public:

        //! returns am instance of the class
        virtual void*           newInstance()      = 0;

        //! returns the super class ID, see nyCommon.h
        virtual long            getSuperClassID()  = 0;

        //! returns the class title string
        virtual char*           getTitle()         = 0;

        //! returns the class name as it is in C++
        virtual char*           getClassName()     = 0;

        //! returns the class description, comments
        virtual char*           getDescription()   = 0;

        //! returns the class version number
        virtual int             getVersion()       = 0;

        //! returns user data pointer
        virtual void*           getUserData()      = 0;
};
 


- cand se apeleaza newInstance prin intermediul PluginManager vei obtine o instanta la acea clasa plugin
- super class ID e un ID de genul: ID_ENTITY, ID_EDITOR, ID_UTILITY etc, care specifica clasa de baza din care plugin-class-ul e derivat
- classname este numele C++ al clasei, exact asa cum se numeste in codul sursa, trebuie specificat neaparat deoarece asta e un fel de UID ( unique ID ), prin care acea clasa este identificata in sistemul de plugins
- restul sunt diverse informatii despre clasa.

Urmatoarea clasa este PluginInfo care tine informatii despre acel DLL-plugin si clasele plugin exportate de acesta:

Cod sursă:

//! Plugin info class that holds the info about a dynamic library plugin
class NYTRO_API PluginInfo
{
    public:

        //! get the number of classes hosted by the plugin
        virtual int                 getClassCount()            = 0;

        //! get the class info object for the class from the aIndex
        virtual PluginClassInfo*    getClassInfo( int aIndex ) = 0;

        //! get the plugin title string
        virtual char*               getTitle()                 = 0;

        //! get the plugin description string
        virtual char*               getDescription()           = 0;

        //! get the plugin copyright string
        virtual char*               getCopyright()             = 0;

        //! get the plugin version
        virtual int                 getVersion()               = 0;

        //! get the plugin package name, some kind of plugin grouping,
        //! for example provider plugins have "PROVIDER_PACKAGE"
        virtual char*               getPackageTypeName()       = 0;

        //! get this plugin's user data
        virtual void*               getUserData()              = 0;
};
 


Se intelege de la sine ce semnificatie are fiecare membru.

Apoi clasa Plugin care tine toate aceste date, dupa ce pluginul a fost incarcat de PluginManager:

Cod sursă:

//! A plugin holder class, from a dynamic library
class NYTRO_API Plugin
{
    public:

        Plugin();
        Plugin( const char* pFile );
        virtual ~Plugin();

        //! returns the dynamic library handle
        LibraryHandle          getLibHandle();

        //! returns the dynamic library file name
        string&                getLibFile();

        //! returns this plugin's info class
        PluginInfo*            getPluginInfoClass();

        //! loads a dynamic library plugin
        Result                 load( const char* pFile );

        //! free the dynamic library plugin
        Result                 free();

        //! sorts and retrieves the plugin classes which have the specified super class ID, into rClasses
        //! return the number of classes found, or zero if none found
        int                    sortBySuperClassID( long aSuperClassID, vector& rClasses );

    private:

        LibraryHandle            m_hLib;
        PluginInfo*              m_pPluginInfo;
        vector m_classesInfo;
        string                   m_libFile;

        //! the plugin entry point exported function from the dynamic library, retrieved with getProcAddress
        PluginInfo*             (*getPluginInfo)();
};
 


La fel e o clasa simpla, cu diverse metode utilitare care nu necesita vreo explicatie Smile.

In cele din urma managerul central care incarca si tine lista plugin-urilor, si care arata cam asa:

Cod sursă:

//! A plugin manager, holds an array of Plugin objects
class NYTRO_API PluginManager
{
    public:

        PluginManager();
        //! creates the object and loads all plugins from pDir using the pFileMask, ex.: "*.nyp"
        PluginManager( const char* pDir, const char* pFileMask );
        virtual ~PluginManager();

        //! load all plugins from pDir using the pFileMask, ex.: "*.nyp"
        int                 loadPlugins( const char* pDir, const char* pFileMask );

        //! free the loaded plugins and their dynamic libraries
        Result              freePlugins();

        //! sorts and retrieves from all the plugins the classes that have super class ID as aSuperID
        //! return the number of found classes
        int                 sortBySuperClassID( long aSuperID, vector& rList );

        //! get the plugins array
        vector&    getPlugins();

        //! get the class info for a specified class name residing in one of the plugins, or NULL if none found
        PluginClassInfo*    getClassInfo( const char* pClass );

    private:

    // ( ) in loc de mai mare si mai mic, un bug in gdro Very Happy
        vector(Plugin*)   m_plugins;
};
 


Inca odata, explicatiile sunt in comentariile metodelor.
In continuare sa facem un exemplu de DLL plugin cu 2 clase exportate:

MyPluginClass1.h
Cod sursă:

class MyPluginClass1 : public Entity
{
    public:
        .................
}
 


MyPluginClass2.h
Cod sursă:

class MyPluginClass2 : public Entity
{
    public:
        .................
}
 


MyPluginPack.cpp
Cod sursă:


class NYTRO_API MyPluginClass1Info : public PluginClassInfo
{                                                                 
    public:                                                       
        void*       newInstance()       { return new MyPluginClass1; }
        long        getSuperClassID()   { return SUPER_ID_ENTITY; }
        char*       getTitle()          { return "My Cool Entity 1"; }       
        char*       getClassName()      { return "MyPluginClass1"; }
        char*       getDescription()    { return "No smart description here"; }
        int         getVersion()        { return 1; }
        void*       getUserData()       { return NULL; }
};

class NYTRO_API MyPluginClass2Info : public PluginClassInfo
{                                                                 
    public:                                                       
        void*       newInstance()       { return new MyPluginClass2; }
        long        getSuperClassID()   { return SUPER_ID_ENTITY; }
        char*       getTitle()          { return "My Cool Entity 2"; }       
        char*       getClassName()      { return "MyPluginClass2"; }
        char*       getDescription()    { return "No smart description here"; }
        int         getVersion()        { return 1; }
        void*       getUserData()       { return NULL; }
};

// declaram cele doua class-info
MyPluginClass1Info g_MyPluginClass1Info;
MyPluginClass2Info g_MyPluginClass2Info;

// vectorul cu lista de clase ale plugin DLL-ului
PluginClassInfo* g_thisPluginClassInfos[] = { &g_MyPluginClass1Info, &g_MyPluginClass2Info };

// clasa care contine informatiile despre acest DLL plugin
class NYTRO_API ThisPluginInfo : public PluginInfo                             
{                                                                               
    public:                                                                     
        int                 getClassCount()             { return sizeof( g_thisPluginClassInfos )
                                                                         sizeof( g_thisPluginClassInfos[0] ); }
        PluginClassInfo*    getClassInfo( int aIndex )  { return g_thisPluginClassInfos[aIndex]; }
        char*               getTitle()                  { return "My Plugin Pack"; }                         
        char*               getDescription()            { return "This plugin pack contains 2 classes! yay!"; }
        char*               getCopyright()              { return "(C) None"; }
        int                 getVersion()                { return 1; }                       
        char*               getPackageTypeName()        { return "ENTITY_PACKAGE"; }
        void*               getUserData()               { return NULL; }                     
};                                                                                               

// declaram obiectul pentru plugin info
ThisPluginInfo g_thisPluginInfo;

// entry-point care va fi chemat de PluginManager, daca nu e gasit atunci acest DLL nu va fi considerat plugin
PluginInfo* NYTRO_API __getPluginInfo() { return (PluginInfo*) &g_thisPluginInfo; }

 


Pentru definirea acelor clase se pot face si niste #define-uri care sa usureze viata, cu ele ar arata cam asa:

Cod sursă:


PLUGIN_CLASS_INFO( MyPluginClass1, SUPER_ID_ENTITY, "My Cool Entity 1", "No smart description here", 1, NULL );
PLUGIN_CLASS_INFO( MyPluginClass2, SUPER_ID_ENTITY, "My Cool Entity 2", "No smart description here", 1, NULL );

BEGIN_PLUGIN_CLASSES
    ADD_PLUGIN_CLASS( MyPluginClass1 )
    ADD_PLUGIN_CLASS( MyPluginClass2 )
END_PLUGIN_CLASSES;

PLUGIN_INFO( "My Plugin Pack", "This plugin pack contains 2 classes! yay!", "(C) None", 1, "ENTITY_PACKAGE", NULL );

 


Utilizarea PluginManager si instantierea unui plugin intr-un game.exe de exemplu:

Cod sursă:

pluginMan->loadPlugins( "/plugins" );
.....................
PluginClassInfo* classInfo = pluginMan->getClassInfo( "MyPluginClass1" );

// daca exista
if( classInfo )
{
    // clasa de baza este entity, pentru ca din ea am derivat MyPluginClass1
    Entity* pluggedEntity = (Entity*)classInfo->newInstance();

    // voila!, new plugin entity, use it!
}

 



Cam asta ar fi, sper ca s-a inteles cat de cat Smile.
Astept intrebari si comentarii.

Comentarii pentru acest articol:



Pagina 2 din 2 [ 1 | 2 ]

Mesaj Info autor
    Postat la 17 May 2007 11:38:07    Subiect: < fara subiect >
c0mas info:

c0mas:

Nu poti sa faci totul in script ... oricat de tare ar fi el.

Poti sa dai access la foarte multe din script, dar in general pentru total conversionuri ... trebuie sa dai si surse.

Si nu e obligatoriu sa faci tot modul un mare dll. Poti sa faci pe langa alte mici dll-uri (care sa le face moderi) si care sa extinda ... tot ce se poate extinde.

Do what you love, money will follow!


Status:
Înregistrat pe:
19 Apr 2007 13:41:50
Vârsta: 35 ani
Mesaje: 337
Locatie: Bucuresti
Programator
Dream Builder Studios
 
    Postat la 17 May 2007 13:35:07    Subiect: < fara subiect >
nekitu info:

nekitu:

ca idee: faci sa zicem mai multe plugin-uri cu entities, std_entities.dll, mygame_specific_ents.dll, eh si in script faci doar behavior rules si tot din script doar dai comenzi si setezi proprietatile acestor entitati, astfel extinzand si mai mult jocul.

SpoOoOoock! Life Is Too Short For Cheap Chocolate


Status:
Înregistrat pe:
29 Sep 2006 11:33:12
Vârsta: 32 ani
Mesaje: 1033
Locatie: Brasov
Programator
7thFACTOR Entertainment Studios
 

Pagina 2 din 2 [ 1 | 2 ]


Server time: 14:33:15 23.05.2012



[ Termeni si conditii | Contact | F.A.Q. | Funny Pictures ]

© 2006 - 2012 Copyright 7thFACTOR Entertainment - All rights reserved