MCDR 插件

什么是 MCDR 插件?

MCDR 插件是位于插件目录中的一个以 .py.mcdr 为后缀的文件,也可以是一个具有特定文件结构的目录。见 插件格式 文档以获得更多有关插件格式的信息

插件目录的列表可以在 配置文件 中定义。启动时,MCDR 会自动加载每个插件目录中的每个插件

你可以通过查阅 样例插件仓库 或者 插件模板仓库 来获得更多的参考

快速开始

打开你设置的 MCDR 的插件目录之一,创建一个名为 HelloWorld.py 的文件

cd my_plugin_folder
touch HelloWorld.py

打开它并输入如下代码:

PLUGIN_METADATA = {
    'id': 'hello_world',
    'version': '1.0.0',
    'name': 'My Hello World Plugin'
}


def on_load(server, old):
    server.logger.info('Hello world!')

返回MCDR控制台,输入 !!MCDR reload plugin 。你应该会看到插件发送了一条 hello world

[TaskExecutor/INFO] [hello_world]: Hello world!

好耶,你成功写出了你的第一个插件!

元数据

元数据提供了插件的基本信息。它是一个包含多个键值对的 json 对象,如:

{
    "id": "example_plugin",
    "version": "1.0.0",
    "name": "Example Plugin",
    "description": "Example plugin for MCDR",
    "author": "Fallen_Breath",
    "link": "https://github.com/MCDReforged/MCDReforged-ExamplePlugin",
    "dependencies": {
       "mcdreforged": ">=2.0.0-alpha.1"
    }
}

不同的 插件格式 有着不同的声明元数据的方法,但其元数据的内容是一致的

参见

元数据 文档

入口点

在 MCDR 加载你的插件时,MCDR 会导入你指定的入口点模块。入口点是你的插件与 MCDR 间的桥梁

单文件插件 而言,入口点就是插件自己。对于 多文件插件,入口点声明于其元数据中,默认值是插件的 id,即位于以插件 id 为名的文件夹中的 __init__.py

举个例子:

MyPlugin.mcdr
    my_plugin/
        __init__.py
        source.py
    mcdreforged.plugin.json

在这个多文件插件中,当入口点的值为默认值时,MCDR 会导入模块 my_plugin,也就是加载 MyPlugin.mcdrmy_plugin/ 文件夹中的 __init__.py 位于 __init__.py 中的 on_load 函数将会被注册为事件监听器

如果入口点被设置为了 my_plugin.source,那么 MCDR 将会导入模块 my_plugin.source,也就是加载 my_plugin/ 文件夹中的 source.py

入口点模块的实例被用于 get_plugin_instance() 中,也是 插件被加载 事件的第二个参数

插件注册表

插件注册表是一个插件注册的东西的集合。在每次加载插件之前,它都会被清空,因此你最好在 插件被加载 事件中注册它们

事件监听器

有 3 种方法可以注册事件侦听器的插件

  1. 使用特定名称在 入口点 模块全局范围内声明一个函数。它是注册监听器的老办法,仅适用于 MCDR 提供的事件。有关详细信息,请查看 默认的事件监听器

    例如,下面广泛使用的函数是默认的 插件被加载 事件监听器

    def on_load(server, prev):
        do_something()
    
  2. 手动调用 register_event_listener() 方法来注册事件监听器。你可以为事件监听器指定可调用对象和优先级

    以下是一些关于手动注册事件监听器的例子:

    def my_on_mcdr_general_info(server, info):
        pass
    
    def on_my_task_done(server, my_task_info, my_task_data):  # the 2nd and 3rd parameter is determined by the plugin that emits this event
        pass
    
    def on_load(server, prev):
        server.register_event_listener('mcdr.general_info', my_on_mcdr_general_info, priority=500)
        server.register_event_listener(MCDRPluginEvents.PLUGIN_UNLOADED, my_on_unload, priority=2000)
        server.register_event_listener('myplugin.task_done', on_my_task_done)
    
  3. 使用 event_listener() 装饰器

指令

除了在如 on_user_info 的用户信息事件回调中手动解析用户输入 info.content,MCDR 还为插件提供了一个指令构建系统来帮助插件注册它们的指令

查看 指令树 文档以获取有关构建指令树的更多详细信息

假设你已经使用根文字节点 root 构建了指令树,则你可以使用 register_command() 方法在 MCDR 中注册这一棵指令树:

server.register_command(root_node)

帮助信息

插件可以使用 register_help_message() 将其帮助消息注册到 MCDR,以便用户使用 !!help 指令 来查看所有插件的帮助消息

翻译

如果你的插件需要处理一些诸如信息本地化的翻译工作,你可以让 MCDR 来帮你:通过 register_translation() 注册一个翻译,并使用 tr() 或者 rtr() 来获取翻译后的字符串

参阅 插件开发的一些提示 中的 翻译 章节关于使用翻译的一些建议

导入插件

在多文件插件被加载时,MCDR 将会把多文件插件的文件路径追加至 sys.path 中。对于打包插件而言,文件路径是 .mcdr 文件的路径;对于文件夹插件而已,文件路径是其文件夹的路径

因此,你可以简单的通过 import 语句导入插件 id 的方式,来导入其他插件。这也是推荐的导入方式,因为这可以为你的 IDE 提供了代码高亮等信息

除此之外,你还可以使用 get_plugin_instance() 方法来导入其他插件的入口点。这也是 导入单文件插件的唯一方法。对于多文件插件而言,其返回的值与直接导入插件是相同的

import my_lib_plugin as libA
libB = server.get_plugin_instance('my_lib_plugin')
print(libA == libB)  # True

别忘了在你的插件元数据中声明你的插件依赖,否则 MCDR 将不能保证插件加载顺序是正确的