Writing Plugins

Getting started

Now that you know which type of plugin you need, you can start writing your own plugin. There is two way to write a Python plugin:

  • Single file plugins: You use a single .py file that you put directly in the plugins folder of the MO2 installation.

  • Module Plugins: You create a Python package (folder) with a __init__.py file that you put in the plugins folder of the MO2 installation.

Most examples of plugins will be module plugins.

Single file plugins

Prior to version 2.3, this was the only way of creating a Python plugin. You simply need to create a myplugin.py file in the plugins folder of Mod Organizer 2 with a content similar to:

import mobase

class MyPlugin(...):
    ...

def createPlugin() -> mobase.IPlugin:
    return MyPlugin()

We will see later on how to create the actual MyPlugin class. The createPlugin function is the function that is called by Mod Organizer 2 to instantiate the plugin.

You can also provide multiple plugins by using createPlugins instead of createPlugin:

from typing import List

import mobase

class MyPlugin1(...):
    ...

class MyPlugin2(...):
    ...


def createPlugins() -> List[mobase.IPlugin]:
    return [MyPlugin1(), MyPlugin2()]

Note: If you provide neither createPlugin() nor createPlugins, MO2 will display an error message in the logs.

Note: If you add a return type-hint to createPlugin() or createPlugins (->), mypy will type-check the function and warn you if one of your plugins is invalid, e.g. if you forgot to implement a required method.

If you need to provide other files with your .py (assets or other Python files), you can put them in the plugins/data, but this is deprecated since MO2 2.3, and you should instead create a Python module plugin.

Module Plugins

Module plugins were introduced in MO2 2.3 and are shipped as whole folder containg a python module. The minimum content of the folder is a __init__.py file with createPlugin or createPlugins function.

A minimal module plugin could be as follows:

plugins/               # MO2 plugins folder
    myplugin/
        __init__.py
        plugin.py

In plugin.py, you could define your plugin:

# plugin.py

import mobase

class MyPlugin(...):
    ...

And in __init__.py, you should write createPlugin:

# __init__.py

import mobase  # For type-checking createPlugin().

from .plugin import MyPlugin  # Always use relative import:

def createPlugin() -> mobase.IPlugin:
    return MyPlugin()

Similar to single-file plugins, you can expose createPlugins instead of createPlugin to instantiate multiple plugins.

Note: The name of the folder does not have to be a valid python package, and you should always use relative imports within the module (import .xxx) instead of absolute ones.

Writing the plugin

IPlugin interface

In the code snippets above, the MyPlugin class was not implemented. Depending on the type of plugins that you want to create, you will need to extend a different class.

class MyTool(mobase.IPluginTool):  # Create a Tool plugin
    ...

class MyPreview(mobase.IPluginPreview):  # Create a preview plugin
    ...

Each plugin class has its own abstract methods that you need to implement but all the classes also extend IPlugin, so you need to implement the methods from IPlugin:

from typing import List

import mobase

class MyPlugin(...):  # The base class depends on the actual type of plugin

    _organizer: mobase.IOrganizer

    def __init__(self):
        super().__init__()  # You need to call this manually.

    def init(self, organizer: mobase.IOrganizer):
        self._organizer = organizer
        return True

    def name(self) -> str:
        return ""

    def author(self) -> str:
        return "Tannin"

    def description(self) -> str:
        return self._tr("Gives a friendly greeting")

    def version(self) -> mobase.VersionInfo:
        return mobase.VersionInfo(1, 0, 0, mobase.ReleaseType.FINAL)

    def isActive(self) -> bool:
        return self._organizer.pluginSetting(self.name(), "enabled")

    def settings(self) -> List[mobase.PluginSetting]:
        return [
            mobase.PluginSetting("enabled", "enable this plugin", True)
        ]

Most of these are pretty simple to understand:

  • name: Returns the name of the plugin. The name of the plugin is used to fetch settings, and in many places, so this should not change between versions.

  • author: Returns the name of the plugin author (you!).

  • description: Returns the description of the plugin.

  • version: Returns the version of the plugin. See VersionInfo for more details.

  • isActive: Returns True if the plugin is active, False otherwise. This usually returns True, unless you want to check for something to dynamically enable the plugin. You can also use a plugin setting to allow users to disable your plugins.

  • settings: Returns the list of settings (that user can modify) for this plugin. Settings can be int`, bool, str or list of str. Here we indicate that we have a “enabled” setting that user could use to disable the plugin (and we use it in isActive).

The __init__ method is the normal Python constructor for our plugin, called when doing MyPlugin(). You should always call super().__init__() explicitly when extending MO2 classes (due to a “bug” in boost.python).

The init method is called by MO2 to initialize the plugin. The given argument, organizer, is an instance of IOrganizer which is the class used to interface with MO2. Here, we use it in the isActive() method to retrieve the “enabled” setting for our plugin. See IOrganizer for more details.