plugins¶
This page contains tutorials about the plugins
package.
PluginInfo¶
For every plugin a plugins.info.PluginInfo
instance can be retrieved,
even if the plugin isn’t loaded. The plugins.info.PluginInfo
instance
is created based on a file called info.ini
that has to reside in the
plugin’s main directory. This file is completely optional. So, if it doesn’t
exist, the plugins.info.PluginInfo
instance will still be created,
but it won’t contain much more information beside the name of the plugin.
Here is an example info.ini
file containing the most basic options:
# A verbose name of the plugin.
# If this option is not defined, the plugin's name will be used, all
# underscores are replaced with spaces and the first letter of every word is
# being capitalized.
verbose_name = "Paintball"
# Name of the Author.
# If this option is not defined, the plugin info will contain 'None'.
author = "Ayuto"
# A description of what the plugin does.
# If this option is not defined, the plugin info will contain 'None'.
description = "Adds paintball effects to the game."
# Version of the plugin.
# If this option is not defined, the plugin info will contain 'unversioned'.
version = "1.3"
# An link to the 'Plugin Releases' forum or the plugin's SPPM link (hopefully
# coming some day).
# If this option is not defined, the plugin info will contain 'None'.
url = "http://forums.sourcepython.com/viewtopic.php?f=7&t=502"
You can also note which permissions are defined by your plugin. This helps server owners to configure their authorization settings properly. See the example below:
# Permissions defined by the plugin.
# If this option is not defined, the plugin info will contain an empty list.
[permissions]
admin.kick = "Ability to kick a player."
admin.ban = "Ability to ban a player."
As soon as a plugin is being loaded, a public console variable is created that contains the following information:
- Name:
<plugin name>_version
- Value:
<version of the plugin>
- Description:
<verbose name> version.
If you don’t want a public console variable, you can simply use the following option in your info file to disable that feature:
public_convar = False
If you wish to use different values to create the public console variable, you can use the following in your info file:
[public_convar]
# All of these options are optional.
name = "my_plugin_version"
value = "My custom value."
description = "My custom description."
Sometimes you might also want to define some custom options for the plugin info. Adding those is quite easy. You just need to define them:
my_custom_option = "something"
my_custom_option2 = "something else"
Since those are custom options, they are not displayed when the list of loaded
plugins is printed (e.g. via sp plugin list
). If you want to change that
behaviour, you can define the display_in_listing
option:
display_in_listing = "my_custom_option", "my_custom_option2"
Retrieving a PluginInfo instance¶
Note
If you retrieve a plugins.info.PluginInfo
instance of a plugin
that isn’t loaded, the plugins.info.PluginInfo
instance is
recreated everytime you retrieve it. Only loaded plugins will cache the
instance.
The following example will show how to retrieve a
plugins.info.PluginInfo
instance for a specific plugin.
from plugins.manager import plugin_manager
# Retrieve the plugin info of the paintball plugin. This is case sensitive!
info = plugin_manager.get_plugin_info('paintball')
# Print the plugin's description
print(info.description)
You are not only restricted to the plugin’s name, but you can also use the plugin’s import path. See the example below:
from plugins.manager import plugin_manager
info = plugin_manager.get_plugin_info('paintball.paintball')
Obviously, this doesn’t make much sense as the first example is shorter and
both result in the same. But this feature has been added, so plugin’s can pass
their own __name__
variable, which contains their import path. Thus, you
can use the following snippet to retrieve the plugin info of your own plugin,
without directly specifying the plugin’s name.
from plugins.manager import plugin_manager
info = plugin_manager.get_plugin_info(__name__)
You can also use this snippet outside of your plugin’s main file (e.g. in other sub-modules or sub-packages).
Adding sub-plugins¶
Adding sub-plugins to your plugin is done a very few steps. All you actually
need is a new instance of the plugins.manager.PluginManager
class.
This instance allows you to load plugins from a specific directory.
Imagine your plugin resides in ../addons/source-python/plugins/my_plugin
and within that directory you have created a new directory called plugins
,
which should contain all sub-plugins of my_plugin
. Then the plugin manager
could be created using the following code:
from plugins.manager import PluginManager
my_plugin_manager = PluginManager('my_plugin.plugins.')
That’s all you need! Now you can load sub-plugins using my_plugin_manager
from your sub-plugins directory with the following code:
# Load the plugin 'my_sub_plugin' from
# ../addons/source-python/plugins/my_plugin/plugins
my_plugin_manager.load('my_sub_plugin')
However, this doesn’t print any messages like Source.Python does when you load
a plugin via sp plugin load
. If you would like to have those messages as
well, without implementing them on your own, you can simply create an instance
of plugins.command.SubCommandManager
.
from plugins.command import SubCommandManager
my_sub_command_manager = SubCommandManager(
# Tell the sub command manager to use this plugin manager to load plugins
my_plugin_manager,
# If you create sub-commands, they will use 'my_plugin' as the base
# command like Source.Python uses 'sp'
'my_plugin'
)
Now, you can load your sub-plugin using the following code:
my_sub_command_manager.load_plugin('my_sub_plugin')
So far, so good. But what if you want to load your plugins via a server command? Well, just add it using the following code:
@my_sub_command_manager.server_sub_command(['plugin', 'load'])
def plugin_load(command_info, plugin):
my_sub_command_manager.load_plugin(plugin)
@my_sub_command_manager.server_sub_command(['plugin', 'unload'])
def plugin_unload(command_info, plugin):
my_sub_command_manager.unload_plugin(plugin)
Now you can also load your sub-plugins using my_plugin plugin load
and
unload them using my_plugin plugin unload
.
There is only one last thing left to do. If your main plugin is being
unloaded, you should also unload all of your sub-plugins. It doesn’t cause any
problems with Source.Python if you don’t do that, because Source.Python also
unloads all core.AutoUnload
and core.WeakAutoUnload
instances of your sub-plugins. But if you don’t do that the unload
functions of your sub-plugins are never getting called. To avoid this issue
use the following code:
def unload():
for plugin in my_plugin_manager.loaded_plugins:
plugin.unload()
Here is the full example code to implement sub-plugins:
from plugins.manager import PluginManager
from plugins.command import SubCommandManager
my_plugin_manager = PluginManager('my_plugin.plugins.')
my_sub_command_manager = SubCommandManager(
# Tell the sub command manager to use this plugin manager to load plugins
my_plugin_manager,
# If you create sub-commands, they will use 'my_plugin' as the base
# command like Source.Python uses 'sp'
'my_plugin'
)
@my_sub_command_manager.server_sub_command(['plugin', 'load'])
def plugin_load(command_info, plugin):
my_sub_command_manager.load_plugin(plugin)
@my_sub_command_manager.server_sub_command(['plugin', 'unload'])
def plugin_unload(command_info, plugin):
my_sub_command_manager.unload_plugin(plugin)
def unload():
for plugin in my_plugin_manager.loaded_plugins:
plugin.unload()