Proposal: Plug-ins for MQ
Posted: Mon Apr 21, 2003 3:16 am
I was thinking it might be a useful idea to create a plug-in interface for MQ. Basically people would just make a DLL that exposes a certain set of functions (or even an Interface). Maybe name the files .MQX. It would have something like this structure:
MQXLoad(POFFSETSINFO *, ...) - Performs any initialization required by the plug-in. OFFSETSINFO would be a new struct or class that would contain all the offsets used by MQ, wrapped up neatly for passing as a parameter. The same thing would need to be done for any other global variables used by the code, so that the plug-in would be fully capable of doing anything MQ does.
MQXMessageHandler(POFFSETSINFO *, PMESSAGEINFO *, ...) - Called at many different points during the MQ execution. The MESSAGEINFO struct would contain a message ID and any other pertinent data. The message ID would correspond to when the function was being called. For example, call it during CCommandHook, CChatHook, etc. Also called every time the main execution loop iterates, when InsertCommands is called, etc. Called at the beginning and/or end of each regular MQ command to support overriding/extending the existing commands. Possibly create a way for the plug-in to generate an error state which is passed back to MQ itself and handled in some way. This function might be better implemented as a callback instead of a message handler.
MQXGetCommands(PCOMMANDS &, ...) - Called to obtain a list of the commands extended or added by the plug-in. These commands would then be automatically added to DetermineWhoFirst and TakeControlOfCommandList. This function would specify a function name to call when each command is run. That way MQ can just directly call the plug-in function as if it was a standard command function in the main code.
MQXGetVersionInfo(...) - Called to get some useful information such as the name of the plug-in, it's author, and it's version. This info could be used at the EQ commandline like: "/listplugins" (or if you're a hardcore Windows programmer: "/enumerateplugins" :) )
MQXUnload(POFFSETSINFO *, ...) - Used for deinitialization.
Which plug-ins to load could be specified in macroquest.ini, and I don't see any reason why we couldn't also have a dynamic load feature such as "/loadplugin <xxx>" and "/unloadplugin <xxx>".
Also might need a way to call certain functions in the MQ codebase, especially WriteChatColor, etc.
I am not 100% an expert in DLL programming, so I have a question -- if EQLIB.DLL uses LoadLibrary to load another DLL, does that DLL automatically get injected into eqgame's process space? So the third-party DLL could directly access memory offsets? Or do we instead need to use ReadProcessMemory to indirectly read the memory without generating a protection fault?
I think this would benefit the MQ project as a whole in 3 ways:
Oh, and before anyone tells me that this idea would slow down the code too much, please do a little bit of Windows Shell programming (or any Windows programming, for that matter); you'll see that this is how it's done. :)
MQXLoad(POFFSETSINFO *, ...) - Performs any initialization required by the plug-in. OFFSETSINFO would be a new struct or class that would contain all the offsets used by MQ, wrapped up neatly for passing as a parameter. The same thing would need to be done for any other global variables used by the code, so that the plug-in would be fully capable of doing anything MQ does.
MQXMessageHandler(POFFSETSINFO *, PMESSAGEINFO *, ...) - Called at many different points during the MQ execution. The MESSAGEINFO struct would contain a message ID and any other pertinent data. The message ID would correspond to when the function was being called. For example, call it during CCommandHook, CChatHook, etc. Also called every time the main execution loop iterates, when InsertCommands is called, etc. Called at the beginning and/or end of each regular MQ command to support overriding/extending the existing commands. Possibly create a way for the plug-in to generate an error state which is passed back to MQ itself and handled in some way. This function might be better implemented as a callback instead of a message handler.
MQXGetCommands(PCOMMANDS &, ...) - Called to obtain a list of the commands extended or added by the plug-in. These commands would then be automatically added to DetermineWhoFirst and TakeControlOfCommandList. This function would specify a function name to call when each command is run. That way MQ can just directly call the plug-in function as if it was a standard command function in the main code.
MQXGetVersionInfo(...) - Called to get some useful information such as the name of the plug-in, it's author, and it's version. This info could be used at the EQ commandline like: "/listplugins" (or if you're a hardcore Windows programmer: "/enumerateplugins" :) )
MQXUnload(POFFSETSINFO *, ...) - Used for deinitialization.
Which plug-ins to load could be specified in macroquest.ini, and I don't see any reason why we couldn't also have a dynamic load feature such as "/loadplugin <xxx>" and "/unloadplugin <xxx>".
Also might need a way to call certain functions in the MQ codebase, especially WriteChatColor, etc.
I am not 100% an expert in DLL programming, so I have a question -- if EQLIB.DLL uses LoadLibrary to load another DLL, does that DLL automatically get injected into eqgame's process space? So the third-party DLL could directly access memory offsets? Or do we instead need to use ReadProcessMemory to indirectly read the memory without generating a protection fault?
I think this would benefit the MQ project as a whole in 3 ways:
- The changes that are necessary would make MQ much more Object Oriented, and thus cleaner and more manageable. It might also assist us with modularizing the code more.
- Having a plug-in capability lets other users relatively easily create new functionality for MQ without a) posting code snippets or b) trying to get their changes added to the official source tree.
- A by-product of this feature would be some useful documentation about the MQ source code in general, as it would be necessary in order to instruct people on writing MQX files.
Oh, and before anyone tells me that this idea would slow down the code too much, please do a little bit of Windows Shell programming (or any Windows programming, for that matter); you'll see that this is how it's done. :)