Plugin API Documentation

Kenny van Vulpen edited this page Mar 14, 2016 · 7 revisions

MiNET ship with a lot of core Minecraft functionality in place already. But we recognize that sometimes you want more, or just that it would work differently. For this purpose MiNET offer a plugin API for .NET that can be used to create, tweak and twist the functionality to fit your specific needs.

NOTE: Both the API and the information on this page is experimental and can change without notice. If you have questions about the API please ask them in our gitter chat (link on the main page).

Functionality supported

The following functionality is currently provided by the API

  • Bootstrapping of server through startup classes.
  • Plugins can implement receive and send packet handlers
  • Plugins can act as command handlers

Hello world plugin

A plugin ultimately must implement the IPlugin interface. However, to make things easier you can base you normal plugins on the Plugin base class and implement only the parts you need.

The following is an example of the least amout of code that is needed to create a new plugin.

    [Plugin]
    public class HelloWorld : Plugin
    {
    }

Command handlers

You can create your own custom commands. These commands are send from the clients as text messages that MiNET will parse and hand over to potential command handlers.

The following example code will listen for the command '/min'.

    [Command]
    public void Min()
    {
    }

Below, a more complete example of how to trigger an explosion with the radius specified.

    [Command]
    [Description("Creates an explosion with the specified radius.")]
    public void Boom(Player player, int radius = 10)
    {
        new Explosion(
            player.Level,
            new BlockCoordinates((int) player.KnownPosition.X, (int) player.KnownPosition.Y, (int) player.KnownPosition.Z),
            radius).Explode();
    }

A couple of things to notice about this example. First of all, usage information (in client) is generated automatically based on the name and parameters of the method. Additionally, a description can be specificed using the DescriptionAttribute. The Player object, if present as the first parameter, is injected automatically. All other method parameters are treated as command parameters. They are automatically converted to the type that is specified for the parameter on interception by MiNET.

The following datatypes can convert automatically

  • string
  • short
  • int
  • bool
  • float
  • double

This greatly reduce the code necessary for the plugin author to write in order to get full functionality and parameter parsing out of the command handler method.

The API also support the following notation in order to bypass the type conversion and send the arguments directly (warning: bug in this right now)

    [Command]
    [Description("Generic command with args.")]
    public void Generic(Player player, string[] args)
    {
        Log.Info("Generic command executed.");
    }

The args parameter contain the parameterlist as specified by the user in the chat-windows of the client.

Receive packet handler

You can intercept incomming packets by implementing methods that receive the packet as a parameter. Any method implemented according to the rules below, will automatically register as receive packet handlers and do without the PacketHandlerAttribute.

  • Method is public
  • Method must have return of type Package
  • Method first parameter is a type derived from Package
  • For a send packet handler, the SendAttribute is always required.
    public Package MinimalHandler(McpeMovePlayer packet)
    {
        return packet; // Handled
    }

Optionally, you can annotate the method with the PacketHandlerAttribute. This might increase the readability of the code, and can also change it's behaviour. Also notice the ReceiveAttribute below, marking this method as receive handler. Methods are defaulted to recieve handlers.

    [PacketHandler]
    [Receive]
    public Package HandleIncomingMove(McpeMovePlayer packet)
    {
        return packet;
    }

In the case you want the plugin to intercept packets that will sent you replace the ReceiveAttribute with the SendAttribute.

    [Send]
    public Package HandleSendMove(McpeMovePlayer packet)
    {
        return packet;
    }

If a handler method return null it will stop further processing of the packet. This works both for send and receive handlers the same.

    [Send]
    public Package HandleSendMove(McpeMovePlayer packet)
    {
        return null; // Halt processing
    }

If the second parameter is of type Player the player for the current request will be injected.

    public Package HandleSendMove(McpeMovePlayer packet, Player player)
    {
        return packet;
    }

Full plugin example

The code below is an example that implements a plugin with a send and receive handler for player move packets.

    [Plugin]
    public class SimplePlugin : Plugin
    {
        [PacketHandler, Receive]
        public Package HandleIncoming(McpeMovePlayer packet)
        {
            return packet; // Process
        }

        [PacketHandler, Send]
        public Package HandleOutgoing(McpeMovePlayer packet)
        {
            return packet; // Send
        }
    }

Bootstrapping of server

A lot of the core functionality of MiNET can be replaced by implementing various provider interfaces. However, in order to replace this functionality, it needs to be reconfigured before the server spring into action. So to cover these scenarios and avoid tedious configuration of the startup, a plugin developer can choose to implement IStartup and bootstrap the server.

Below a full example of how the identity management in the server can be completely replaced via simple code configuration.

    [Plugin]
    public class StartupPlugin : Plugin, IStartup
    {
        private static readonly ILog Log = LogManager.GetLogger(typeof (StartupPlugin));

        /// <summary>
        /// Startup class for MiNET. Example sets the user and role managers and stores 
        /// for the application.
        /// </summary>
        /// <param name="server"></param>
        public void Configure(MiNetServer server)
        {
            server.UserManager = new UserManager<User>(new DefaultUserStore());
            server.RoleManager = new RoleManager<Role>(new DefaultRoleStore());
            Log.Info("Executed startup successfully. Replaced identity managment.");
        }
    }

If you want to know more about identity management in MiNET, please see the official ASP.NET identity documentation:

Events

MiNET also offers a couple of Events to make a few things a bit easier.

    var server = Context.Server;

    server.LevelManager.LevelCreated += (sender, args) =>
    {
        Level level = args.Level;
        level.BlockBreak += (o, eventArgs) => { };
        level.BlockPlace += (o, eventArgs) => { };
    };

    server.PlayerFactory.PlayerCreated += (sender, args) =>
    {
        Player player = args.Player;
        player.PlayerJoin += (o, eventArgs) => eventArgs.Player.Level.BroadcastMessage($"{ChatColors.Gold}[{ChatColors.Green}+{ChatColors.Gold}]{ChatFormatting.Reset} {eventArgs.Player.Username}");
        player.PlayerLeave += (o, eventArgs) => eventArgs.Player.Level.BroadcastMessage($"{ChatColors.Gold}[{ChatColors.Red}-{ChatColors.Gold}]{ChatFormatting.Reset} {eventArgs.Player.Username}");
    };

More examples