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 1 din 2 [ 1 | 2 ]

Mesaj Info autor
    Postat la 14 May 2007 13:20:02    Subiect: < fara subiect >
Dark info:

Dark:

Am si eu doua intrebari de circotas:

1. La ce folosesc plug-in-urile intr-un joc? Atita timp cit jocul are tot timpul aceleasi feature-uri, deci va incarca tot timpul aceleasi plug-in-uri, de ce sari peste linker si ii muti munca la runtime?

2. (Disclaimer: intrebarea asta nu se aplica pentru hobby/indie, da' nu scrie la inceputul articolului ca-i doar pentru segmentul ala) Ce faci pe platformele fara DLL-uri, cum ar fi toate mai putin PC-ul (si anume toate in afara de cea mai putin profitabila)?

Ultima editare efectuată de Dark pe 14 May 2007 13:20:15; 1 editări în total

"Am crezut ca esti ceva mai avansat" - Nekitu, 2008 A.D.
Autobaza


Status:
Înregistrat pe:
12 May 2007 20:12:30
Vârsta: ? ani
Mesaje: 740
Locatie:
Programator

 
    Postat la 14 May 2007 13:32:06    Subiect: < fara subiect >
nekitu info:

nekitu:

1. chestie de gust Smile, grupare, poti extinde un joc doar aruncand un dll plugin acolo, fara sa recompilezi tot

2. se pot emula sisteme de plugins si pe celelalte, si daca chiar nu merge poti avea sistemul de plugins linked intr-un mare exe, cu sau fara DLL, asa mai raspund si la 1. a doua oara Smile, ideea e ca ai totul generic.

Ultima editare efectuată de nekitu pe 14 May 2007 14:02:53; 1 editări în total

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
 
    Postat la 15 May 2007 19:42:07    Subiect: < fara subiect >
Icebreaker info:

Icebreaker:

Sistemul de plugin-uri are avantajele lui, dar folosind un limbaj de scripting ai flexibilitate mai mare si poti modifica orice cu un simplu editor de text. Deci si game designer-i pot modifica cu usurinta un script, iar mai greu pot compila un DLL. IMHO!

There are only 10 types of people in the world, those who understand binary, and those who don't!


Status:
Înregistrat pe:
24 Mar 2007 18:56:33
Vârsta: 25 ani
Mesaje: 3
Locatie: Romania
Programator

 
    Postat la 16 May 2007 11:24:20    Subiect: < fara subiect >
Pintea info:

Pintea:

Daca vrei sa faci un ENGINE sau un JOC MODABIL (nu doar un joc care e "tot timpu la fel") pluginurile sunt bune...cat despre ce zicea anteposteurul meu intr-adevar un script ar avea mai multe avantaje, insa necesita mai multa munca din partea celui care face jocul (acum sa nu imi spui ca iau LUA de pe net si gata am rezolvat), munca ce poate fi suprimata cu un sistem de pluginuri.

PS:
Bravo pentru articol si la mai multe, iar carcotasii de profesie ar trebui sa ne arate printr-un articol ce cred ei ca ar fi folositor ptr un joc.

Ultima editare efectuată de Pintea pe 16 May 2007 11:27:12; 1 editări în total



Status:
Înregistrat pe:
05 May 2007 14:50:45
Vârsta: 29 ani
Mesaje: 301
Locatie:
Programator

 
    Postat la 16 May 2007 12:23:10    Subiect: < fara subiect >
Icebreaker info:

Icebreaker:

Pai iei LUA de pe NET, exporti functiile necesare catre EL, si cam atat. Oricum merge mai repede decat sa scrii un sistem de plugin-uri de la zero.

Daca scrii in limbaj de scripting de la zero atunci intradevar necesita *mult* mai multa munca!!!

Ultima editare efectuată de Icebreaker pe 16 May 2007 12:32:05; 1 editări în total

There are only 10 types of people in the world, those who understand binary, and those who don't!


Status:
Înregistrat pe:
24 Mar 2007 18:56:33
Vârsta: 25 ani
Mesaje: 3
Locatie: Romania
Programator

 
    Postat la 16 May 2007 13:07:12    Subiect: < fara subiect >
meeshoo info:

meeshoo:

Nu cred ca cele doua sisteme se exclud. Un sistem de plug-ins iti ofera un engine extensibil, dar poti exporta spre un sistem de scripting functionalitatea plugin-urilor.



Status:
Înregistrat pe:
15 May 2007 10:52:43
Vârsta: 29 ani
Mesaje: 390
Locatie: Cluj-Napoca
Programator
Jungle Troll Entertainment
 
    Postat la 16 May 2007 14:40:30    Subiect: < fara subiect >
Icebreaker info:

Icebreaker:

Da meeshoo ai dreptate, dar de cele mai multe ori un engine are 1 DLL pentru randare, 1 pentru fizica, 1 pentru partea audio (fmod, bass, etc), 1 pentru scripting, si restul se scripteaza hardcore separat pentru fiecare proiect care foloseste engine-ul respectiv. Iar de efectele special se ocupa shaderele. Nu trebuie sa te atingi de codul engine-ului. Very Happy

There are only 10 types of people in the world, those who understand binary, and those who don't!


Status:
Înregistrat pe:
24 Mar 2007 18:56:33
Vârsta: 25 ani
Mesaje: 3
Locatie: Romania
Programator

 
    Postat la 16 May 2007 15:00:32    Subiect: Re:
nekitu info:

nekitu:

Icebreaker a scris:

Da meeshoo ai dreptate, dar de cele mai multe ori un engine are 1 DLL pentru randare, 1 pentru fizica, 1 pentru partea audio (fmod, bass, etc), 1 pentru scripting, si restul se scripteaza hardcore separat pentru fiecare proiect care foloseste engine-ul respectiv. Iar de efectele special se ocupa shaderele. Nu trebuie sa te atingi de codul engine-ului. Very Happy


cam aia e. Smile, plugins-urile sunt cod rapid, scripturile sunt pt inflorituri si chestii mai mult de setari si triggering decat de executare operatii care iau timp de CPU mult.

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
 
    Postat la 16 May 2007 17:13:42    Subiect: < fara subiect >
c0mas info:

c0mas:

Ar mai fi o utilitate:

Un joc nu inseamna doar jocul respectiv, mai are si o gramada de tooluri pe langa. Iar daca vrei sa folosesti enginul pentru mai multe tipuri de jocuri, probabil va trebui sa modifici si toolurile in functie de tipul jocului.
Dar DACA FOLOSESTI DLL-uri ca sa modifici comportamentul unor editoare generice ... VOILA ai un engine cu un set de tooluri si o lista de packuri pentru diferite tipuri de jocuri.

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 16 May 2007 21:02:13    Subiect: < fara subiect >
Dark info:

Dark:

Cam cite jocuri modabile cu plug-in-uri stiti sa-mi ziceti? Wink

Aia cu editorul generic care poate fi folosit la mai multe proiecte e o idee frumoasa, da' din ce-am vazut se cam aplica vorba aia cu "ends up in tears more often than not". Oricum nu e chiar acelasi lucru sa ai plug-in-uri de editor specifice jocului cu a avea plug-in-uri pe care le incarca bietul joc la runtime.

PS: de unde obsesia asta cu "un engine are 1 DLL pentru randare, 1 pentru fizica, 1 pentru partea audio, 1 pentru scripting"? Care engine? Sau era vorba de ogre.dll, newton.dll, fmod.dll, lua.dll etc.? Smile

"Am crezut ca esti ceva mai avansat" - Nekitu, 2008 A.D.
Autobaza


Status:
Înregistrat pe:
12 May 2007 20:12:30
Vârsta: ? ani
Mesaje: 740
Locatie:
Programator

 
    Postat la 16 May 2007 21:09:50    Subiect: < fara subiect >
nekitu info:

nekitu:

da, alea sunt 3rdparty libs, nu e engine, Unreal engine parca asa e din cate imi aduc aminte, la fel si Nebula Device, sau altele, faza e ca cel putin in cazul meu, eu pot compila intr-un big exe toate pluginurile in final release, si sa le las ca dll-uri separate cand fac debug si sunt in development state, e mai usor asa, iar in final production coc un exe mare cu toate inauntru, pentru ca pluginurile nu trebuie sa fie loaded dintr-un DLL neaparat, le poti adauga din acelasi proiect. simple as that. ah si Dark, vrei sa zici ca tu nu utilizezi 3rdparty ? mi se pare cea mai buna metoda sa utilizezi 3rdparty STABIL, nu toate panaramele de libs, acum toti fac asta, chiar si cei mari.

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
 
    Postat la 16 May 2007 23:15:58    Subiect: < fara subiect >
Dark info:

Dark:

Nu, vreau sa spun ca daca DLL-urile erau de genul celor din exemplul meu,     Icebreaker n-a fost extrem de on-topic.

PS (iar): ma amuza amplasarea lui Nebula Device ala (despre ca nu auzisem pina acum, dar nu stiam ce ratam, inca o combinatie fericita intre nemti si 3D) linga Unreal. Sau imi scapa mie vreo subtilitate?

"Am crezut ca esti ceva mai avansat" - Nekitu, 2008 A.D.
Autobaza


Status:
Înregistrat pe:
12 May 2007 20:12:30
Vârsta: ? ani
Mesaje: 740
Locatie:
Programator

 
    Postat la 16 May 2007 23:40:55    Subiect: < fara subiect >
nekitu info:

nekitu:

Nebula Device e un engine destul de nice si free pe deasupra, ar trebui sa te uiti cam ce face. Alaturarea celor doua nu are nimic subtil, am dat doar doua exemple de engine-uri care utilizeaza paradigma de plugins.

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
 
    Postat la 17 May 2007 00:40:28    Subiect: Re:
c0mas info:

c0mas:

Dark a scris:

Cam cite jocuri modabile cu plug-in-uri stiti sa-mi ziceti? Wink

Aia cu editorul generic care poate fi folosit la mai multe proiecte e o idee frumoasa, da' din ce-am vazut se cam aplica vorba aia cu "ends up in tears more often than not". Oricum nu e chiar acelasi lucru sa ai plug-in-uri de editor specifice jocului cu a avea plug-in-uri pe care le incarca bietul joc la runtime.


In principiu daca vrei sa dai un joc modabil la care sa dai si o parte de surse ... acele surse ar cam trebui sa fie dll.

La faza cu editorul ... probabil sunt mai multe (eu personal am in plan un mic system de dll-uri la 2 dintre editoare, ... dar inca nu a fost esential).

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 11:25:27    Subiect: < fara subiect >
Dark info:

Dark:

Da, evident ca daca vrei sa dai surse C++, mod-urile vor ajunge DLL-uri (sau optional, cine stie ce bytecode, ca la unii care fac shootere). Da' nu mi se pare ca cgame.dll e genul de plug-in despre care se vorbeste aici.

Pe de alta parte, mult mai multe jocuri modabile isi expun matele sub forma unui limbaj de scripting oarecare decit sub forma de cod C++.

"Am crezut ca esti ceva mai avansat" - Nekitu, 2008 A.D.
Autobaza


Status:
Înregistrat pe:
12 May 2007 20:12:30
Vârsta: ? ani
Mesaje: 740
Locatie:
Programator

 

Pagina 1 din 2 [ 1 | 2 ]


Server time: 14:32:48 23.05.2012



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

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