New "plugins" package available!!!

Official Announcements about Source.Python.
User avatar
satoon101
Project Leader
Posts: 2697
Joined: Sat Jul 07, 2012 1:59 am

New "plugins" package available!!!

Postby satoon101 » Sat Jan 11, 2014 4:00 am

First off, sorry for the lull in development, everyone. We are now back and working on adding in more functionality, along with a few other "internal" major changes (coming soon).

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:

    Syntax: Select all

    from plugins.command import SubCommandManager

    class _SPSubCommandManager(SubCommandManager):
    manager = SPPluginManager
    instance = SPLoadedPlugin
    logger = _CoreCommandLogger
  • GunGame:

    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:

    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
  • GunGame:

    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:

    Syntax: Select all

    from plugins.manager import PluginManager

    class _SPPluginManager(PluginManager):
    logger = _CoreCommandLogger

    SPPluginManager = _SPPluginManager()
  • GunGame:

    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)
The above code makes sure that the given plugin name is valid and then gets its type (whether included or custom) in order to set the proper "base_import" value. Then, once it has done this, it calls the super class' (PluginManager) __missing__ method to do the rest of the work.

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:

    Syntax: Select all

    from plugins.instance import LoadedPlugin

    class SPLoadedPlugin(LoadedPlugin):
    logger = _CoreCommandLogger
  • GunGame:

    Syntax: Select all

    from plugins.instance import LoadedPlugin

    class GGLoadedPlugin(LoadedPlugin):
    logger = GGPluginsInstanceLogger
Please note that you do not want to get "the" instance of this class. Each plugin, when loaded, will automatically have its own instance of your class created. This is the value that the PluginManager will automatically set the plugin name's value to in its dictionary.

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.

Return to “News & Announcements”

Who is online

Users browsing this forum: No registered users and 16 guests