🔒 Locked
Plug-in system
Advertisement
Original Post
Hello After reading these two posts * [Scripting Idea] * [Why use scripting in games?] I have come to the conclusion that i don't need a scripting language but a plug-in system. But I’m not sure where to start. Any thoughts, links, tutorials, examples, or documentation I tired google but was unsuccessful in finding anything useful Thank you for your time edit: i'm using C++
A plugin system is usually done with DLLs (or your platforms equivalent). The DLLs export functions which your program uses to do certain things. You linked to a thread where the OP is using C++ so I'm gonna assume you're using it to.
In C++ the normal way to make a plugin is to define an interface. This is generally a struct (though you can just use a class and make everything public), that contains only pure virtual functions, e.g.
This plugin would be used for printing messages. Now in your DLL you implement this interface, i.e. you inherit from it and write code for the virtual functions, you also define a function called something like create_simplePlugin which returns an iSimplePlugin*, so when you want to load a plugin, you load up the DLL it is contained in and grab the appropiate create function. You then use the create function to create the plugin, which returns a pointer to its interface and then you can use the plugin. With this system you can implement iSimplePlugin however you want and your program is always given the same interface no matter what the implementation. Using a different implementation is simply a matter of loading a different DLL.
In C++ the normal way to make a plugin is to define an interface. This is generally a struct (though you can just use a class and make everything public), that contains only pure virtual functions, e.g.
struct iSimplePlugin{ bool initPlugin( void ) = 0; void printMessage(std::string msg) = 0; void shutdownPlugin( void ) = 0;};This plugin would be used for printing messages. Now in your DLL you implement this interface, i.e. you inherit from it and write code for the virtual functions, you also define a function called something like create_simplePlugin which returns an iSimplePlugin*, so when you want to load a plugin, you load up the DLL it is contained in and grab the appropiate create function. You then use the create function to create the plugin, which returns a pointer to its interface and then you can use the plugin. With this system you can implement iSimplePlugin however you want and your program is always given the same interface no matter what the implementation. Using a different implementation is simply a matter of loading a different DLL.
I use the sort of the same thing for my current project, I simply search the plugin folder and its sub-folders for all DLLs, loads each one and calls one function defined within each one of them. This function fills in in a struct I use inside the program:
struct StructName
{
void* (*pfnCreateFunc)(void); // function to return pointer to interface for this plugin
void (*pfnDeleteFunc)(void*); // function to delete this interface
char* pszPluginName; // name of this plugin
}
Well there is alot more to it, but this is the basic idea.
struct StructName
{
void* (*pfnCreateFunc)(void); // function to return pointer to interface for this plugin
void (*pfnDeleteFunc)(void*); // function to delete this interface
char* pszPluginName; // name of this plugin
}
Well there is alot more to it, but this is the basic idea.
I use an RTTI-ish system:
now, you can do something like
when the DLL (or EXE) that CEnemey's code is defined in, that class entry will be added to the map (and, when the dll is freed, the class entry will be removed). so in the game part, I can do:
That is the basic idea. There is a metric fuckton of other stuff you can add to this that is pretty useful, but eventually you will reinvent part of the gigantic wheel that is .Net. I built a crapload of things into that system and then .Net came along.
that code was off the top of my head, so there are probably mistakes in there.
edit: here is my CClass:
as you can see, there is a ton of stuff you can add that is pretty convenient, and that doesn't include the serialization (that is part of the IProperty stuff there)
I forgot to mention, it uses a little trick to insure the static std::map is constructed before any class tries to register. That static function is simply this:
when anything tries to get the map, it is gaurenteed to be created.
edit again: the general idea behind this is ripped off from MFC, so there is another place to look for more information.
edit once more: removed the useless table-raping comments
class CClass{ typedef IObject * (*CreateProc)(); std::string m_Name; static std::map < std::string, CClass * > RegisteredClasses;public: CClass(const char * Name, CreateProc Create) : New(Create), m_Name(Name) { // add this to the map } ~CClass() { // remove this from the map } const CreateProc New; static CClass * Load(const char * Name, const char * Library) { // if class isn't already loaded, load the library and then check the map again }};#define DECLARE_CLASS(Name) static CClass Name##Class; static IObject * Create() { return new Name(); }#define IMPLEMENT_CLASS(Name) CClass Name::Name##Class(Name, Name::Create);now, you can do something like
// h file:class CEnemy : public CGameObject // derives from IObject somewhere along the hiearchy{ DECLARE_CLASS(CEnemy); ...};// cpp file:IMPLEMENT_CLASS(CEnemey);when the DLL (or EXE) that CEnemey's code is defined in, that class entry will be added to the map (and, when the dll is freed, the class entry will be removed). so in the game part, I can do:
CClass * EnemyClass = CClass::Load("CEnemey", "EnemeyDll.dll");if(EnemeyClass){ CGameObject * NewEnemy = (CGameObject *)EnemeyClass->Create(); // NewEnemey is an instance of CEnemy}That is the basic idea. There is a metric fuckton of other stuff you can add to this that is pretty useful, but eventually you will reinvent part of the gigantic wheel that is .Net. I built a crapload of things into that system and then .Net came along.
that code was off the top of my head, so there are probably mistakes in there.
edit: here is my CClass:
class ENGINE_API CClass { typedef std::map < CString,class CClass * > tClassRegistry; static tClassRegistry & GetRegistry(); typedef IObject * (*CreateFunc)(); static IObject * NoFactoryCreateFunc(); IProperty m_RootProperty; int m_Flags; int m_Size; CString m_Key; CString m_Library; CString m_Name; CClass * m_Parent; CClass(const CClass & NoCopy); // copy constructor bad public: CClass(const TCHAR * Name, CClass * Parent, const TCHAR * Library, CreateFunc cF, int Size, int Flags); ~CClass(); static CString MakeName(const TCHAR*Cls, const TCHAR*L); int GetFlags() const; // gets the class flags CString GetClass() const; CString GetLibrary() const; CString GetName() const; CClass * GetParent(); const CClass * GetParent() const; IProperty * GetProperty(); int SizeOf() const; const CreateFunc New; void LoadConfig(IObject * O, IConfig * Cf = NULL, const TCHAR * Sec = NULL); void SaveConfig(IObject * O, IConfig * Cf = NULL, const TCHAR * Sec = NULL); void RegConsole(IObject * O, IConsole * C = NULL, const TCHAR * RootName = NULL); void UnRegConsole(IObject * O, IConsole * C = NULL, const TCHAR * RootName = NULL); bool IsA(const CClass * Other) const; CClass * Base(CClass * Other); const CClass * Base(const CClass * Other) const; static int IterateClasses(IClassIterator * Iterator, CClass * MinClass = NULL); static int ClassCount(CClass * MinClass = NULL); static CClass * Load(const TCHAR * Class, bool LoadLib = true); static CClass * Load(const TCHAR * Library, const TCHAR * Class, bool LoadLib = true); ENGINE_API friend IArchive & operator << (IArchive & Buf, CClass *& Class); };as you can see, there is a ton of stuff you can add that is pretty convenient, and that doesn't include the serialization (that is part of the IProperty stuff there)
I forgot to mention, it uses a little trick to insure the static std::map is constructed before any class tries to register. That static function is simply this:
CClass::tClassRegistry & CClass::GetRegistry() { static tClassRegistry Registry; return Registry; }when anything tries to get the map, it is gaurenteed to be created.
edit again: the general idea behind this is ripped off from MFC, so there is another place to look for more information.
edit once more: removed the useless table-raping comments
The overhead is the same as calling a function through a pointer or calling a virtual function. There is extra overhead when the DLL is first loaded and you call GetProcAddress, but that should only be done infrequently.
You might also want to check out the July 2004 issue of C/C++ Users Journal, or at least the downloadable code for the issue. There's an article on dynamic plug-ins.
Also, I've been working on a way to have a simple, dynamic entity system using DLLs and factories. So far I have the ability to create an entity from a DLL, but I'm stuck on going through the filesystem to find all the DLLs. I can bring in what I have and send it to you tomorrow, but it's Windows specific. (The CUJ article will help me make it Win/*nix, though.)
Also, I've been working on a way to have a simple, dynamic entity system using DLLs and factories. So far I have the ability to create an entity from a DLL, but I'm stuck on going through the filesystem to find all the DLLs. I can bring in what I have and send it to you tomorrow, but it's Windows specific. (The CUJ article will help me make it Win/*nix, though.)
Or you could utilise COM under Windows :)
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement