|
| 1 | +plugins |
| 2 | +======= |
| 3 | + |
| 4 | +This page contains tutorials about the :mod:`plugins` package. |
| 5 | + |
| 6 | + |
| 7 | +PluginInfo |
| 8 | +---------- |
| 9 | + |
| 10 | +For every plugin a :class:`plugins.info.PluginInfo` instance can be retrieved, |
| 11 | +even if the plugin isn't loaded. The :class:`plugins.info.PluginInfo` instance |
| 12 | +is created based on a file called ``info.ini`` that has to reside in the |
| 13 | +plugin's main directory. This file is completely optional. So, if it doesn't |
| 14 | +exist, the :class:`plugins.info.PluginInfo` instance will still be created, |
| 15 | +but it won't contain much more information beside the name of the plugin. |
| 16 | + |
| 17 | +Here is an example ``info.ini`` file containing the most basic options: |
| 18 | + |
| 19 | +.. code:: ini |
| 20 | +
|
| 21 | + # A verbose name of the plugin. |
| 22 | + # If this option is not defined, the plugin's name will be used, all |
| 23 | + # underscores are replaced with spaces and the first letter of every word is |
| 24 | + # being capitalized. |
| 25 | + verbose_name = "Paintball" |
| 26 | +
|
| 27 | + # Name of the Author. |
| 28 | + # If this option is not defined, the plugin info will contain 'None'. |
| 29 | + author = "Ayuto" |
| 30 | +
|
| 31 | + # A description of what the plugin does. |
| 32 | + # If this option is not defined, the plugin info will contain 'None'. |
| 33 | + description = "Adds paintball effects to the game." |
| 34 | +
|
| 35 | + # Version of the plugin. |
| 36 | + # If this option is not defined, the plugin info will contain 'unversioned'. |
| 37 | + version = "1.3" |
| 38 | +
|
| 39 | + # An link to the 'Plugin Releases' forum or the plugin's SPPM link (hopefully |
| 40 | + # coming some day). |
| 41 | + # If this option is not defined, the plugin info will contain 'None'. |
| 42 | + url = "http://forums.sourcepython.com/viewtopic.php?f=7&t=502" |
| 43 | +
|
| 44 | +
|
| 45 | +You can also note which permissions are defined by your plugin. This helps |
| 46 | +server owners to configure their authorization settings properly. See the |
| 47 | +example below: |
| 48 | + |
| 49 | +.. code:: ini |
| 50 | +
|
| 51 | + # Permissions defined by the plugin. |
| 52 | + # If this option is not defined, the plugin info will contain an empty list. |
| 53 | + [permissions] |
| 54 | + admin.kick = "Ability to kick a player." |
| 55 | + admin.ban = "Ability to ban a player." |
| 56 | +
|
| 57 | +
|
| 58 | +As soon as a plugin is being loaded, a public console variable is created that |
| 59 | +contains the following information: |
| 60 | + |
| 61 | +* Name: ``<plugin name>_version`` |
| 62 | +* Value: ``<version of the plugin>`` |
| 63 | +* Description: ``<verbose name> version.`` |
| 64 | + |
| 65 | + |
| 66 | +If you don't want a public console variable, you can simply use the following |
| 67 | +option in your info file to disable that feature: |
| 68 | + |
| 69 | +.. code:: ini |
| 70 | +
|
| 71 | + public_convar = False |
| 72 | +
|
| 73 | +
|
| 74 | +If you wish to use different values to create the public console variable, you |
| 75 | +can use the following in your info file: |
| 76 | + |
| 77 | +.. code:: ini |
| 78 | +
|
| 79 | + [public_convar] |
| 80 | + # All of these options are optional. |
| 81 | + name = "my_plugin_version" |
| 82 | + value = "My custom value." |
| 83 | + description = "My custom description." |
| 84 | +
|
| 85 | +
|
| 86 | +Sometimes you might also want to define some custom options for the plugin |
| 87 | +info. Adding those is quite easy. You just need to define them: |
| 88 | + |
| 89 | +.. code:: ini |
| 90 | +
|
| 91 | + my_custom_option = "something" |
| 92 | + my_custom_option2 = "something else" |
| 93 | +
|
| 94 | +
|
| 95 | +Since those are custom options, they are not displayed when the list of loaded |
| 96 | +plugins is printed (e.g. via ``sp plugin list``). If you want to change that |
| 97 | +behaviour, you can define the ``display_in_listing`` option: |
| 98 | + |
| 99 | +.. code:: ini |
| 100 | +
|
| 101 | + display_in_listing = "my_custom_option", "my_custom_option2" |
| 102 | +
|
| 103 | +
|
| 104 | +Retrieving a PluginInfo instance |
| 105 | +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 106 | + |
| 107 | +.. note:: |
| 108 | + |
| 109 | + If you retrieve a :class:`plugins.info.PluginInfo` instance of a plugin |
| 110 | + that isn't loaded, the :class:`plugins.info.PluginInfo` instance is |
| 111 | + recreated everytime you retrieve it. Only loaded plugins will cache the |
| 112 | + instance. |
| 113 | + |
| 114 | + |
| 115 | +The following example will show how to retrieve a |
| 116 | +:class:`plugins.info.PluginInfo` instance for a specific plugin. |
| 117 | + |
| 118 | +.. code:: python |
| 119 | +
|
| 120 | + from plugins.manager import plugin_manager |
| 121 | +
|
| 122 | + # Retrieve the plugin info of the paintball plugin. This is case sensitive! |
| 123 | + info = plugin_manager.get_plugin_info('paintball') |
| 124 | +
|
| 125 | + # Print the plugin's description |
| 126 | + print(info.description) |
| 127 | +
|
| 128 | +
|
| 129 | +You are not only restricted to the plugin's name, but you can also use the |
| 130 | +plugin's import path. See the example below: |
| 131 | + |
| 132 | +.. code:: python |
| 133 | +
|
| 134 | + from plugins.manager import plugin_manager |
| 135 | +
|
| 136 | + info = plugin_manager.get_plugin_info('paintball.paintball') |
| 137 | +
|
| 138 | +
|
| 139 | +Obviously, this doesn't make much sense as the first example is shorter and |
| 140 | +both result in the same. But this feature has been added, so plugin's can pass |
| 141 | +their own ``__name__`` variable, which contains their import path. Thus, you |
| 142 | +can use the following snippet to retrieve the plugin info of your own plugin, |
| 143 | +without directly specifying the plugin's name. |
| 144 | + |
| 145 | +.. code:: python |
| 146 | +
|
| 147 | + from plugins.manager import plugin_manager |
| 148 | +
|
| 149 | + info = plugin_manager.get_plugin_info(__name__) |
| 150 | +
|
| 151 | +
|
| 152 | +You can also use this snippet outside of your plugin's main file (e.g. in |
| 153 | +other sub-modules or sub-packages). |
| 154 | + |
| 155 | + |
| 156 | +Adding sub-plugins |
| 157 | +------------------ |
| 158 | + |
| 159 | +Adding sub-plugins to your plugin is done a very few steps. All you actually |
| 160 | +need is a new instance of the :class:`plugins.manager.PluginManager` class. |
| 161 | +This instance allows you to load plugins from a specifc directory. |
| 162 | + |
| 163 | +Imagine your plugin resides in ``../addons/source-python/plugins/my_plugin`` |
| 164 | +and within that directory you have created a new directory called ``plugins``, |
| 165 | +which should contain all sub-plugins of ``my_plugin``. Then the plugin manager |
| 166 | +could be created using the following code: |
| 167 | + |
| 168 | +.. code:: python |
| 169 | +
|
| 170 | + from plugins.manager import PluginManager |
| 171 | +
|
| 172 | + my_plugin_manager = PluginManager('my_plugin.plugins.') |
| 173 | +
|
| 174 | +
|
| 175 | +That's all you need! Now you can load sub-plugins using ``my_plugin_manager`` |
| 176 | +from your sub-plugins directory with the following code: |
| 177 | + |
| 178 | +.. code:: python |
| 179 | +
|
| 180 | + # Load the plugin 'my_sub_plugin' from |
| 181 | + # ../addons/source-python/plugins/my_plugin/plugins |
| 182 | + my_plugin_manager.load('my_sub_plugin') |
| 183 | +
|
| 184 | +
|
| 185 | +However, this doesn't print any messages like Source.Python does when you load |
| 186 | +a plugin via ``sp plugin load``. If you would like to have those messages as |
| 187 | +well, without implementing them on your own, you can simply create an instance |
| 188 | +of ``plugins.command.SubCommandManager``. |
| 189 | + |
| 190 | +.. code:: python |
| 191 | +
|
| 192 | + from plugins.command import SubCommandManager |
| 193 | +
|
| 194 | + my_sub_command_manager = SubCommandManager( |
| 195 | + # Tell the sub command manager to use this plugin manager to load plugins |
| 196 | + my_plugin_manager, |
| 197 | +
|
| 198 | + # If you create sub-commands, they will use 'my_plugin' as the base |
| 199 | + # command like Source.Python uses 'sp' |
| 200 | + 'my_plugin' |
| 201 | + ) |
| 202 | +
|
| 203 | +
|
| 204 | +Now, you can load your sub-plugin using the following code: |
| 205 | + |
| 206 | +.. code:: python |
| 207 | +
|
| 208 | + my_sub_command_manager.load_plugin('my_sub_plugin') |
| 209 | +
|
| 210 | +
|
| 211 | +So far, so good. But what if you want to load your plugins via a server |
| 212 | +command? Well, just add it using the following code: |
| 213 | + |
| 214 | +.. code:: python |
| 215 | +
|
| 216 | + @my_sub_command_manager.server_sub_command(['plugin', 'load']) |
| 217 | + def plugin_load(command_info, plugin): |
| 218 | + my_sub_command_manager.load_plugin(plugin) |
| 219 | +
|
| 220 | + @my_sub_command_manager.server_sub_command(['plugin', 'unload']) |
| 221 | + def plugin_unload(command_info, plugin): |
| 222 | + my_sub_command_manager.unload_plugin(plugin) |
| 223 | +
|
| 224 | +
|
| 225 | +Now you can also load your sub-plugins using ``my_plugin plugin load`` and |
| 226 | +unload them using ``my_plugin plugin unload``. |
| 227 | + |
| 228 | +There is only one last thing left to do. If your main plugin is being |
| 229 | +unloaded, you should also unload all of your sub-plugins. It doesn't cause any |
| 230 | +problems with Source.Python if you don't do that, because Source.Python also |
| 231 | +unloads all :class:`core.AutoUnload` and :class:`core.WeakAutoUnload` |
| 232 | +instances of your sub-plugins. But if you don't do that the ``unload`` |
| 233 | +functions of your sub-plugins are never getting called. To avoid this issue |
| 234 | +use the following code: |
| 235 | + |
| 236 | +.. code:: python |
| 237 | +
|
| 238 | + def unload(): |
| 239 | + for plugin in tuple(my_plugin_manager.values()): |
| 240 | + plugin.unload() |
| 241 | +
|
| 242 | +
|
| 243 | +Here is the full example code to implement sub-plugins: |
| 244 | + |
| 245 | +.. code:: python |
| 246 | +
|
| 247 | + from plugins.manager import PluginManager |
| 248 | + from plugins.command import SubCommandManager |
| 249 | +
|
| 250 | + my_plugin_manager = PluginManager('my_plugin.plugins.') |
| 251 | + my_sub_command_manager = SubCommandManager( |
| 252 | + # Tell the sub command manager to use this plugin manager to load plugins |
| 253 | + my_plugin_manager, |
| 254 | +
|
| 255 | + # If you create sub-commands, they will use 'my_plugin' as the base |
| 256 | + # command like Source.Python uses 'sp' |
| 257 | + 'my_plugin' |
| 258 | + ) |
| 259 | +
|
| 260 | + @my_sub_command_manager.server_sub_command(['plugin', 'load']) |
| 261 | + def plugin_load(command_info, plugin): |
| 262 | + my_sub_command_manager.load_plugin(plugin) |
| 263 | +
|
| 264 | + @my_sub_command_manager.server_sub_command(['plugin', 'unload']) |
| 265 | + def plugin_unload(command_info, plugin): |
| 266 | + my_sub_command_manager.unload_plugin(plugin) |
| 267 | +
|
| 268 | + def unload(): |
| 269 | + for plugin in tuple(my_plugin_manager.values()): |
| 270 | + plugin.unload() |
0 commit comments