Skip to main content
gamedev.net
🔒 Locked

Plug-in system

Started by funvill 9 replies 1.4k views
Original Post
funvill
funvill
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++
Monder
Monder
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.

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.
Deebo
Deebo
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.
Kibble
Kibble
I use an RTTI-ish system:
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
funvill
funvill
WoW,
thank you everyone for the great responses,
the plug-in idea is a lot more clearer now then it was before.

Paradigm Shifter
Paradigm Shifter
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.
"Most people think, great God will come from the sky, take away everything, and make everybody feel high" - Bob Marley
Spudder
Spudder
SPL is a specialised plug-in library which you might wanna check out.
coldacid
coldacid
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.)
funvill
funvill
re: Spudder

That’s perfect and so simple to implement.
Thank you, Thank you, Thank you
====Funvill[Home|Tiny xml|Boost|Wiki|STL]====================

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.