BlazeBans API/Building an addon

What an addon is

An addon is an ordinary Paper plugin that depends on BlazeBans and registers itself so operators can see it. There is no special loader, no manifest key, and no sandbox.

The only thing that makes it an addon rather than a plugin that happens to use the API is that it declares itself, and behaves consistently with the rest of the BlazeBans install.

Registering

java
import net.blazebans.api.BlazeBansProvider;
import net.blazebans.api.addon.BlazeBansAddon;

private static final String ADDON_ID = "MyAddon";

@Override
public void onEnable() {
    BlazeBansProvider.registerAddon(new BlazeBansAddon(
        ADDON_ID,
        "My Addon",
        getPluginMeta().getVersion(),
        "Adds custom punishment effects.",
        "enabled"
    ));
}

@Override
public void onDisable() {
    BlazeBansProvider.unregisterAddon(ADDON_ID);
}

Your addon then appears in /blazebans addons.

The five fields

FieldPurpose
pluginIdStable identifier. Cannot be blank
nameDisplay name. Cannot be blank
versionYour version. getPluginMeta().getVersion() is right
descriptionOne line saying what it does
statusFree text, shown as-is

IDs are trimmed and compared case-insensitively, so MyAddon and myaddon are the same entry. Registering an ID that already exists replaces its metadata rather than adding a second row.

Both pluginId and name throw IllegalArgumentException if blank, so do not pass a config value straight in without checking it.

Use status honestly

status is free text and it is the first place an operator looks when something is not working. Make it say something.

java
String status = this.voiceChatHooked ? "enabled" : "waiting for Simple Voice Chat";

"enabled" when everything is fine, and a specific reason when it is not, turns a support question into a self-answer.

To update it later, register again with the same ID.

Always unregister

onDisable should always remove your entry, even on a partial startup:

java
@Override
public void onDisable() {
    BlazeBansProvider.unregisterAddon(ADDON_ID);
}

This is safe when BlazeBans is already gone. It only touches a static map, so it will not throw during shutdown.

Do not start database work in onDisable. See Getting the API.

Console output

AddonConsole gives you BlazeBans' console styling so your addon does not look like a stranger in the log.

java
import net.blazebans.api.console.AddonConsole;

AddonConsole console = new AddonConsole(
    "MyAddon",
    component -> getServer().getConsoleSender().sendMessage(component)
);

console.step("Loading integration...");
console.success("Integration enabled.");
console.info("Watching 4 worlds.");
console.warning("Optional dependency was not found.");
console.error("Integration failed.", exception);
console.blank();

Every line is prefixed [BlazeBans MyAddon] and coloured by level: blaze for step, green for success, amber for warning, red for error.

error(String, Throwable) prints the stack trace in the same style, which beats a raw printStackTrace() in the middle of a coloured log.

There is a three-argument constructor taking a second Consumer<String> for terminals where you want BlazeBans' 24-bit ANSI serialisation instead of Adventure components. Most addons want the two-argument form.

The addon name cannot be blank, and it should be short. It appears on every line.

Conventions worth following

None of this is enforced. It is what the shipped addons do, and matching it makes yours feel like part of the same product.

Namespace your permissions. The shipped addons use blazebans.addon.<addon>.<action>:

yaml
permissions:
  blazebans.addon.myaddon.use:
    description: Allows using the addon's commands.
    default: op
  blazebans.addon.myaddon.reload:
    description: Allows reloading the addon.
    default: op

Keep your own config. The API does not expose BlazeBans' configuration, and you should not read its files. Ship your own settings.yml in your own data folder.

Support a reload. /<addon> reload is what operators expect, and it saves a restart while they tune.

Match the message style. If your addon sends player-facing text, MiniMessage with a configurable colour scheme keeps it consistent with BlazeBans. The shipped addons mirror the same {color_positive} and {meta_prefix_*} placeholder shape.

Fail visibly. If an optional dependency is missing, say so once in console with warning and set your status accordingly. Do not fail silently and do not warn every tick.

Hard depend, and why

yaml
depend:
  - BlazeBans
softdepend:
  - voicechat

depend on BlazeBans, softdepend on anything else you integrate with. That gives you a guaranteed load order for the thing you cannot work without, and graceful degradation for the things you can.

The shipped addons as reference

Two addons ship with BlazeBans, both built on this same public API:

Voice chat adds VOICE_MUTE punishments through Simple Voice Chat. It shows the pattern for an addon introducing a new punishment type and hooking a third-party plugin.

Ban effects plays an animation before a ban is enforced. It shows enforcement delays, per-effect configuration, and console commands on completion.

Both are documented in Addons. Neither uses anything you cannot.

Build one end to end walks through a complete working addon from an empty directory.