Having said that, I have finally finished developing the plugins package that was requested a few months back. With this new package, you can now easily load subscripts for your plugins. I have been testing it using my current working version of GunGame to make sure everything is functioning properly. If you find issues/improvements, please feel free to post a Pull Request on the GitHub repository, and we will take a look at it.
The first portion of the plugins package I wish to discuss is the SubCommandManager. Both SP itself and GunGame utilize the SubCommandManager to start the loading/unloading process. If you wish to use your own method, that is fine as well. As a preface, all of the following examples will be both with the current SP implementation and the GunGame one, as well.
If you wish to use the SubCommandManager, you need to make a class that inherits from SubCommandManager and provide the PluginManager and LoadedPlugin classes (those two classes yet to come) you wish to use. You can also specify the logger you wish to use, but if you do not provide one, the default PluginsCommandLogger will be used. This will be the same with the translations used (though that functionality has not been added to this class as of yet):
- Source.Python:
- GunGame :
Syntax: Select all
from plugins.command import SubCommandManager
class _SPSubCommandManager(SubCommandManager):
manager = SPPluginManager
instance = SPLoadedPlugin
logger = _CoreCommandLogger
Syntax: Select all
from plugins.command import SubCommandManager
class _GGSubCommandManager(SubCommandManager):
manager = GGPluginManager
instance = GGLoadedPlugin
logger = GGPluginCommandLogger
Now that you have the class, you need to get the instance of it. To get an instance, you must provide the main server command and its description. Once you get the instance of it, you will want to use it like a dictionary and add keys (sub-commands) with values (functions/instance methods) to tell the manager what to call when the specified sub-command is used:
- Source.Python:
- GunGame :
Syntax: Select all
SPSubCommandManager = _SPSubCommandManager('sp', 'Source.Python base command.')
SPSubCommandManager['load'] = SPSubCommandManager.load_plugin
SPSubCommandManager['unload'] = SPSubCommandManager.unload_plugin
SPSubCommandManager['reload'] = SPSubCommandManager.reload_plugin
Syntax: Select all
GGSubCommandManager = _GGSubCommandManager('gg', 'GunGame base command.')
GGSubCommandManager['load'] = GGSubCommandManager.load_plugin
GGSubCommandManager['unload'] = GGSubCommandManager.unload_plugin
GGSubCommandManager['reload'] = GGSubCommandManager.reload_plugin
SubCommandManager itself already contains the methods load_plugin, unload_plugin, reload_plugin, print_plugins (sub-command list), and print_help (sub-command help). Therefore, you do not need to write any of these methods directly into your new class. If you wish to change how any of them work, then and only then would you need to add them in. If you wish to add other methods to be used for other sub-commands, those will obviously need to be added in. For instance, in SPSubCommandManager, we have added the subcommand "credits", so that when someone types "sp credits" in the server console, they will get a printout of all the people who have contributed to the plugin. The method created for this purpose is called print_credits, and it is added to the dictionary as such:
Syntax: Select all
SPSubCommandManager['credits'] = SPSubCommandManager.print_credits
Again, you do not have to load your subscripts using sub-commands like this, but if you choose to, that functionality is there for you to take advantage of.
The next step in the process is the PluginManager class. The SubCommandManager class calls the PluginManager class by adding the plugin, by name, to the PluginManager. If the plugin's name is not in the manager, the manager will attempt to load it. PluginManager classes do not require any attributes, but you can set the logger and translations to be used. Once you have created the class, you will also want to get the instance of it. PluginManager instances take exactly one argument (except for SP itself). You must provide base import path. Note that the path is not a directory. For instance, if GunGame solely used ../gungame/plugins/<plugin_name> as the path to sub-scripts, the base import path would be "gungame.plugins.". This is not exactly the case with GunGame, which is why my example is a little different:
- Source.Python:
- GunGame :
Syntax: Select all
from plugins.manager import PluginManager
class _SPPluginManager(PluginManager):
logger = _CoreCommandLogger
SPPluginManager = _SPPluginManager()
Syntax: Select all
from plugins.manager import PluginManager
class _GGPluginManager(PluginManager):
logger = GGPluginsManagerLogger
_base_import = 'gungame.plugins.'
GGPluginManager = _GGPluginManager('gungame')
Notice in the GunGame example that I actually set a new private attribute to the "base" import. GunGame uses both included and custom subscripts, so we modify a few of the built-in methods to make sure the proper values are used:
Syntax: Select all
def __missing__(self, plugin_name):
if not plugin_name in ValidPlugins.all:
raise
self.base_import = (self._base_import +
ValidPlugins.get_plugin_type(plugin_name) + '.')
return super(_GGPluginManager, self).__missing__(plugin_name)
If you only have one path for all of your subscripts, just simply provide that on instantiation and you will be just fine.
The last class to discuss is the LoadedPlugin class. You can easily just use this class itself, as all of its work is done on instantiation. If you wish to provide your own logger or translations, you will need to create your own class:
- Source.Python:
- GunGame :
Syntax: Select all
from plugins.instance import LoadedPlugin
class SPLoadedPlugin(LoadedPlugin):
logger = _CoreCommandLogger
Syntax: Select all
from plugins.instance import LoadedPlugin
class GGLoadedPlugin(LoadedPlugin):
logger = GGPluginsInstanceLogger
One more note. In all the above examples, all "logger" values should be created using the LogManager class. All "translations" values should be a LangStrings instance. If you do use your own translations, please look at each class internally to know which string names you "must" include in your translations file.