Plugin Development - 1attila/Conduit GitHub Wiki
Conduits offers Plugins as a simple interfaces to build complex commands that can be used in-game. Currently plugins only support commands and subcommands with permissions, but in the future they will handle background tasks, configs, permanent variables etc.
A plugin requires 2 things:
-
metadata.json
: contains the name, description, authors, versions etc - code: python files that contains the code
Note
The conduit_dev
plugin can setup these files directly for you with a single command!
-
Install it with
!!plugin install conduit_dev
in-game orhandler.download-plugin conduit_dev
from the console -
Join the server in which the plugin is active and type
!! cdev generate <plugin-name>
Copy this and paste it into metadata.json:
{
"name": "MyFirstPlugin",
"authors": ["1attila"],
"url": "",
"entrypoint": "plugin.py",
"required_plugins": [],
"version": "0.0.0",
"description": "My first Conduit plugin",
"dependencies": [],
"python_version": "",
"supported_langs": ["en_us"]
}
And edit with your specific data.
Metadata descrpiton:
-
name
: this is the name that will used to download/load etc the plugin with the commands -
authors (optional)
: a list containing the names of the authors -
url (optional)
: the url of the plugin (this could be something like a website or a github repo) -
entrypoint
: the file that contains the plugin class that Conduit will load -
required_plugins (optional, unused)
: this will be used to install other plugins as dependencies -
version
: the plugin version, the format must beSTABLE.MAJOR.MINOR
-
description
: simple description of what the plugin does -
dependencies (optional)
: python dependencies -
python_version (optional, unused)
: the python version that the plugin can run -
supported langs
: the languages the plugins supports,en_us
is required
Inside the entrypoint you have specified, you can define your plugin like so:
from mconduit import plugins
class MyFirstPlugin(plugins.Plugin):
...
Conduit is smart enough find and create an istance of your plugin.
Note
If you want to define your own __init__
method, you can only use these parameters:
def __init__(self, manager, metadata, lang) -> "Plugin"
and you must call super().__init__(manager, metadata, lang)
in it.
After your plugin has been setup, you can start defining your own command tree. This is a very short tutorial to introduce you what you can do, to see all the details look at the APIs wiki.
You can define commands as functions by using the command
decorator of the plugins
module like so:
from mconduit import plugins, text, Context
class MyFirstPlugin(plugins.Plugin):
@plugins.command
def test(self, ctx: Context):
ctx.reply(text.yellow(f"Hello {ctx.player.name}"))
Let's break down this code:
- we are defining a command called
test
by using the decorator - the function must accept at least 2 parameters:
-
self
that refers to the plugin -
Context
gives you important informations (like the player who invoked the command, time, etc) and provides some utility methods that you will use frequently
-
- what the command does is replying to the user (equivalent to
/tellraw @<player> <text>
) a yellow text - the reply method can accept simple strings or MinecraftJson text like the one it's used here
- we are sending some yellow text containing hello + the name of the player who invoked the command (we could have used just
ctx.player
sincePlayer
's class string method returns it's name)
Note
The Context
parameter must be annotated!
As you can see you can do quite a lot of stuffs with very few and clear lines of code!
What i've showed about the text, Context and commands API it's very minimal, i suggest you to take a look at their own wiki to know how powerful they are!
In the same way we can declare commands with parameters like so:
from mconduit import plugins, text, Context
class MyFirstPlugin(plugins.Plugin):
@plugins.command
def test(self, ctx: Context):
ctx.reply(text.yellow(f"Hello {ctx.player.name}"))
@plugins.command
def say(self, ctx: Context, string: str)
ctx.say(string)
As you can see the only thing you need to do is adding the parameter into your function with the type annotation.
Conduit matches and casts the parameter already for you.
It's also very flexible and can work with lists of variable length and default parameters togheter like this:
@plugins.command
def t1(self, ctx: Context, arg1: str="a", arg2: List[str]=[], arg3: str="b"):
print(arg1, arg2, arg3)
Conduit also provides some useful types: Flag
and Range
. Their name it's pretty self-explanatory.
If you want to create a subcommand you can still use the command
decorator like so:
from mconduit import plugins, text, Context
class MyFirstPlugin(plugins.Plugin):
@plugins.command
def cmd1(self, ctx: Context):
ctx.reply(text.yellow(f"Hello {ctx.player.name}"))
@cmd1.command
def cmd2(self, ctx: Context, string: str)
ctx.say(string)
Let's break this down:
- we are declaring cmd1
- we are declaring cmd2 as subcommand of cmd1
If you want to limit the command only to certain people you can use permissions. You can set a permission like so:
from mconduit import plugins, text, Context
class MyFirstPlugin(plugins.Plugin):
@plugins.perms(plugins.Permissions.Owner)
@plugins.command
def test(self, ctx: Context):
ctx.reply(text.yellow(f"Hello {ctx.player.name}"))
In this case we have limited the command (and all it's subcommands) to the Owner. To see what permissions are and how they works exactly look at their specific wiki.
If you want to personalize your command further you can do that by using the command()
of the plugins
module method before the command, like the normal command
decorator:
from mconduit import plugins, text, Context
class MyFirstPlugin(plugins.Plugin):
@plugins.command(
name="test",
aliases=["t"],
checks=[plugins.check_perms(plugins.Permission.Owner)]
)
def _test(self, ctx: Context):
ctx.reply(text.yellow(f"Hello {ctx.player.name}"))
Let's break this down:
- the
command()
function provides a decorator that transforms your function into a command - the name provided as argument is the new command name that will be used to invoke the command (and it's not
_test
anymore) - aliases it's an array of names that can be used to call the command
- checks contains all the checks that the user must match to invoke the command (in this case it must be an owner)
Conduit is also able to retrieve and dispatch events!
There are many events you can find in event.py
inside mconduit
folder.
This is a simple example on how to link a function to an event:
from mconduit import plugins, Context
class MyFirstPlugin(plugins.Plugin):
@plugins.event
def on_player_join(self, ctx: Context):
ctx.reply("Hello!")
When the event is not specified it's always the name of the function (like commands), but you can specify it like so:
from mconduit import plugins, Context, Event
class MyFirstPlugin(plugins.Plugin):
@plugins.event(event=Event.PlayerJoin)
def greet(self, ctx: Context):
ctx.reply("Hello!")
Note that there are three type of events:
- Player events: the ones we just saw
- Server events: server start/stop
- Conduit events: conduit start/stop
Based on the type of the event, the function you want to link must have different parameters:
- Player events: Context
- Server evenst: Server
- Conduit events: Handler
Here there are a couple examples:
from mconduit import plugins, Context, Event, Handler, Server
class MyFirstPlugin(plugins.Plugin):
# Player Event
@plugins.event(Event.PlayerDeath)
def on_death(self, ctx: Context):
print("Player death")
# Conduit Event
@plugins.event(Event.ConduitStart)
def on_cstart(self, ctx: Handler):
print("Conduit start")
# Server Event
@plugins.event(Event.ServerStart)
def on_sstart(self, ctx: Server):
print("Server start")
Note that you can link multiple functions with the same event:
from mconduit import plugins, Context, Event
class MyFirstPlugin(plugins.Plugin):
def __init__(self, manager, metadata, lang) -> "MyFirstPlugin":
self.__online_players = []
super().__init__(manager, metadata, lang)
@plugins.event(event=Event.PlayerJoin)
def greet(self, ctx: Context):
ctx.reply("Hello!")
@plugins.event(event=Event.PlayerJoin)
def add_online_players(self, ctx: Context):
self.__online_players.append(ctx.player)
What it's shown here are just the most important features for plugins, but it's not even 1/10 of what you can do! Conduit also provides APIs to load custom settings, manage persistant variables, communicate between other plugins, execute any type of command, fetch a lot of data and a lot more only with a single line of code each! I encorage you to look at the full wiki to get a good idea of what you can do!
When you plugin is ready you can publish it on the Catalogue!
Before doing that i recomend to take a look at the other plugins present in the Catalogue, to give you an idea of how they are made/strucutred.
If you want to publish your plugin into the Catalogue, you can contact attila via discord and if everything is ok the Catalogue will be updated.