Skip to content

Developing custom addons

Matthias Werning edited this page Oct 16, 2016 · 15 revisions

Add-ons extend the functionality of HeavySpleef and allow the server owner to install custom content for HeavySpleef. This documentation should help you to get started with the add-on development.

This part covers the creation of a basic add-on which just prints out a message when HeavySpleef is starting.

Creating Your First Basic Add-on

Setup

To get started you have to add HeavySpleef as a dependency. If you are using Maven (which is highly recommended), please add the following repository and dependency to your pom.xml:

<repositories>
  <repository>
    <id>matzes-repo</id>
    <url>http://repo.xaniox.de/artifactory/release/</url>
  </repository>
</repositories>
<dependencies>
  <dependency>
    <groupId>de.xaniox</groupId>
    <artifactId>heavyspleef-api</artifactId>
    <version>2.4.1</version>
    <scope>provided</scope>
  </dependency>
</dependencies>

In case you aren't using Maven, make sure to add the latest release version accessible from here to your project's build path if you are using Eclipse. Adding jars to the classpath of your project may differ from IDE to IDE.

After you added HeavySpleef as a dependency we can continue with the next section.

Extending The BasicAddon Class

The structure of an add-on is very similar to a Bukkit plugin. Create a class and let it extend the BasicAddon class. This might look like this:

package com.example.addon;

import de.xaniox.heavyspleef.addon.java.BasicAddOn;

public class ExampleAddon extends BasicAddOn {
    
    @Override
    public void enable() {
        getLogger().info("Example add-on has been activated!");
    }

}

The BasicAddon#enable() method will be called when your add-on is being enabled. Please note, that your enable() method may be called at any time while the server running if the admin decides to disable/enable your add-on dynamically via a command.

The next thing you want to do is to add an addon.yml description file to your add-on. Similar to creating a plugin.yml for your Bukkit plugin, the addon.yml is the replacement for add-ons and is placed into the source folder of your add-on (For Maven in the resources folder). A basic addon.yml may look like this:

name: ExampleAddon
version: 1.0
author: xaniox

main: com.example.addon.ExampleAddon

For a detailed attribute reference that may be declared in the addon.yml visit the documentation https://github.com/xaniox/HeavySpleef/wiki/Addon-YAML.

That's all for now! Compile the code, put the generated jar into the plugins/HeavySpleef/addons folder and HeavySpleef will load your add-on.

Commands

Adding a Spleef command is really easy. After creating a class for your command, create a method that should be called when your desired command is executed. Command methods must be annotated using the @Command annotation (Make sure the use the right import) with attributes. An example command method might look like this:

package com.example.addon;

import de.xaniox.heavyspleef.commands.base.*;

public class ExampleCommand {

    @Command(name = "example", usage = "/spleef example", 
             description = "A nice example command!", permission = "heavyspleef.example"
    public void onExampleCommand(CommandContext context, HeavySpleef heavySpleef, ExampleAddon addon) {
         context.getSender().sendMessage("You executed the example command!");
    }

}

This method must be registered at the CommandManagerAccess interface:

@Override
public void enable() {
    getCommandManager().registerSpleefCommand(ExampleCommand.class, this);
}

Flags

Adding a custom flag is a bit more complicated than adding a command. Flags basically small extensions which can be applied to a single game. There is for instance a flag that rewards players on win for reaching a certain place. A flag often stores a single value to operate.

After creating a new class you can decide between different data type presets:

  • BaseFlag (No datatype)
  • BooleanFlag
  • IntegerFlag
  • DoubleFlag
  • LocationFlag
  • StringFlag
  • ItemStackFlag
  • EnumListFlag
  • LocationListFlag
  • ItemListFlag

Choose one of them or create an own implementation of the datatype you need by extending the raw AbstractFlag class.

Every flag class must be annotated with @Flag, which provides basic information about the flag itself. A basic skeleton class can look like this:

@Flag(name = "winner-message")
public class FlagWinnerMessage extends StringFlag {
    
    /* Mandatory empty constructor */
    public FlagWinnerMessage() {}

    @Override
    public void getDescription(List<String> description) {

    }

}

Tip: Your flag should always provide an empty constructor since it is instantiated by reflection when it is added to a game or deserialized on HeavySpleef load.

Now let's move on to the content of the flag. As we are defining a winner message in this example we want to listen for an event which occurs when a Spleef game ends. HeavySpleef implements its own event system which is similar to Bukkit's.

Every event method must be annotated with @Subscribe and defines an event for the first parameter. In our case it looks like this:

@Subscribe
public void onGameWin(PlayerWinGameEvent event) {
    String message = getValue();

    for (SpleefPlayer player : event.getWinners()) {
        player.sendMessage(message);
    }
}

For a list of all available events head to the javadocs

If your flag requires to listen to Bukkit events, you can annotate your flag class with @BukkitListener. But please note that the scope of Bukkit events is global. While HeavySpleef events are bound to a certain game, a Bukkit listener method may be called twice or more often, depending on the amount of games where your flag has been applied. This means that you have to check if the Bukkit event that is fired really pertains to the flag's game.

You can retrieve an instance of the game by adding a field like this to your flag class:

@Inject private Game game;

For more information which methods can be overriden when creating a flag, please head to the javadocs

If you add commands in the flag class, make sure they are static. Furthermore command methods in flags do not need to be registered, instead set the hasCommands parameter in the @Flag to true.

Finally you need to register your flag. You can do this by declaring the flag in your addon.yml or alternatively you may register the flag programmatically. If you choose the first way head to this section for further details. If you choose to add the flag programmatically use this code snippet in your enable() method:

getFlagRegistry().registerFlag(YourClass, this);

Extensions

Extensions differentiate from flags in the way that they basically exist in the phsyical Minecraft world. An example for an extension is the LobbyWall which consists of a row of signs in the world.

The creation of an extension is very similar to a flag. Instead an extension extends the GameExtension class and is annotated with @Extension which contains an internal name for your extension.

Adding and removal of the extension must be handled by yourself, e.g. you probably have to create custom commands for adding and removing an extension. Make sure to set the hasCommands parameter for the @Extension annotation to true if you define command methods inside the extension class. Access to the HeavySpleef instance or to the game it is attached to may be granted by using getHeavySpleef() and getGame().

As there are no presets for Extensions available, serialization and deserialization needs to be handled by yourself. This is done by overriding the GameExtension#marshal(Element) and the GameExtension#unmarshal(Element) methods.

This is a skeleton class for an extension:

//Name is for internal use only (serialization & deserialization)
@Extension(name = "example-extension", hasCommands = true)
public class ExampleExtension extends GameExtension {
    
    //Static command methods for extension adding/removal go here

    @Override
    public void marshal(Element element) {
        //Serialize all necessary attributes for your extension here
    }

    @Override
    public void unmarshal(Element element) {
        //And vice-versa deserialize them from here and assign deserialized
        //values to your extension's fields
    }
    
    //Listening for a GameStateChangeEvent here
    @Subscribe
    public void onGameStateChange(GameStateChangeEvent event) {
        //Do something here
    }
 
}

Registering extensions is almost the same as it is for flags. Either define the class in your addon.yml or add it programmatically via:

getExtensionRegistry().registerExtension(YourClass, this);

Internationalization / Multi-Language Support

HeavySpleef provides a super easy to use API for handling multi-language support in your add-on. All you have to do is to create files in the following format as a resource:
locale_<langugage-code>_<country-code>.yml

For example this would be a resource file for an English language file:
locale_en_US.yml

Or for German:
locale_de_DE.yml

A language resource file might look like this:

some-message-key: 'This is the first message.'
another-message-key: 'This is another message!'
message-with-variable: 'Another great message from $[player]'

You can use color codes prefixed with & in your language file.
Access to messages is granted via I18N#getString(String) or I18N#getVarString(String). The var-string method has the benefit to replace variables declared in the message by a value.

Getting the last message of the example resource file located above would be:

getI18n().getVarString("message-with-variable")
    .setVariable("player", "Foo")
    .toString();

This returns the above message with $[player] replaced by Foo. Access to your add-on i18n is granted by the add-on itself calling BasicAddOn#getI18n() or in a flag via AbstractFlag#getI18N()

Examples

For a full add-on sample visit the example project:
https://github.com/xaniox/heavyspleef-example-addon

For more examples you can also have a look at the official HeavySpleef add-ons:
https://github.com/xaniox/HeavySpleef/tree/master/addons


If you have question regarding the development of add-ons feel free to ask them at our development section at the HeavySpleef forum.

Clone this wiki locally