Addons API - CryptoMorin/KingdomsX GitHub Wiki
Addons are no different than normal plugins, however sometimes these isolated software are purely designed for kingdoms plugin. If you made a compatbility addon for kingdoms, you can ask me to add it too!
This page only contains information about API that is mostly not found in a normal plugin, so a lot of other information is on the main API page instead.
First, you'd need to implement the Addon
class from kingdoms. This is usually done in the same class that JavaPlugin
is extended.
Before going deeper in the features, there are already many open source addons like Peace Treaties and Outposts that you can take a look at.
First of all, you need to be registering all your Namespace'd values onLoad because you cannot register them later.
Another important thing is to look if the plugin was correctly enabled
Here's an overview of things and where they should go:
public final class OutpostAddon extends JavaPlugin implements Addon {
@Override
public void onDisable() {
if (!loaded) return;
signalDisable(); // Save data if you registered custom data models.
disableAddon();
}
@Override
public void onLoad() {
if (!isKingdomsLoaded()) return; // If something wen't wrong with kingdoms, skip.
// All namespaced registries must be registered here in onLoad().
Kingdoms.get().getPermissionRegistery().register(new Namespace("CUSTOMADDON", "BLAH"));
Kingdoms.get().getAuditLogRegistry().register(LogCustomLog.PROVIDER);
LanguageManager.registerMessenger(CustomAddonLang.class); // More info below
}
@Override
public void onEnable() {
if (!isKingdomsEnabled()) {
// If something wen't wrong with kingdoms, don't start.
getLogger().severe("Kingdoms plugin didn't load correctly. Disabling...");
Bukkit.getPluginManager().disablePlugin(this);
return;
}
reloadAddon(); // we can reuse this method since we're just registering a command in it.
registerAddon();
loaded = true;
}
@Override
public void reloadAddon() { // on /k reload
// re-register commands.
new MyAddonCustomCommand();
}
@Override
public String getAddonName() {
return "custom addon";
}
@Override
public File getFile() {
return super.getFile();
}
}
You can register new configs like this and optionally register them for file watchers like this.
If you want to complete your config suite, you can optionally add new config validators for config schemas if you have any new data like this.
You can register /k custom
with the following code:
public class MyAddonCustomCommand extends KingdomsCommand {
public MyAddonCustomCommand() {
super("custom");
}
@Override
public CommandResult execute(CommandContext context) {
if (context.assertPlayer()) return; // The command must be executed by a player.
if (context.requireArgs(1)) return; // The command requires at least 1 argument.
// ...
return CommandResult.SUCCESS;
}
@Override
public @NonNull List<String> tabComplete(@NonNull CommandTabContext context) {
return context.tabCompete("oneOption", "anotherOption");
}
}
Important
Don't forget to register your custom command when both enabling your addon and reloading it. Just creating an instance of the class is enough to register it.
@Override
public void onEnable() {
new MyAddonCustomCommand();
}
@Override
public void onLoad() {
new MyAddonCustomCommand();
}
Now let's see how we can register /k custom create
Note
The execute and tab completion methods are hidden here to but they must be implemented.
However, for parent commands like /k custom
you don't need to implement these methods
as the plugin will automatically generate help pages and tab completion for sub-commands automatically.
But you still need to provide implementation sub-commands that are not grouping commands such as /k custom create
public class MyAddonCustomCommandCreate extends KingdomsCommand {
public MyAddonCustomCommandCreate(KingdomsParentCommand parent) {
super("create", parent);
}
}
public class MyAddonCustomParentCommand extends KingdomsParentCommand {
public MyAddonCustomParentCommand() {
super("custom");
if (isDisabled()) return; // Prevent registering sub-commands if the parent command is disabled in config.
new MyAddonCustomCommandCreate(this);
}
}
Now all you need to do is to new MyAddonCustomParentCommand()
and it'll automatically register sub-commands.
Do you want to use Kingdom's language parser model for parsing placeholders and other advanced coloring and formatting features? You can use Messenger
classes. The most basic way to send a kingdom-parsed message would be the following:
Player player = ...;
Messenger msg = new StaticMessenger("&2Hello, my kingdom's name is &9%kingdoms_kingdom_name%");
msg.sendMessage(player);
This is cool and all, but if you would like to make these messages translatable and configurable through standard language files, you'd have to use DefinedMessenger
and an easy way to use this would be an enum just like the default KingdomsLang
, specially if you have a lot of entries. You can find a full example for Outposts addon in OutpostsLang.java.
public enum CustomAddonLang implements DefinedMessenger {
COMMAND_CUSTOMADDON_CREATE_BLAH("{$sCreates blah.", 1, 2, 3),
CUSTOMADDON_BLAH_BLAH("{$e}Blah {$es}Blah?", 1, 2)
;
private final LanguageEntry languageEntry;
private final String defaultValue;
OutpostsLang(String defaultValue, int... group) {
this.defaultValue = defaultValue;
this.languageEntry = DefinedMessenger.getEntry("custom-addon", this, group);
}
@NonNull
@Override
public LanguageEntry getLanguageEntry() {
return languageEntry;
}
@Override
public String getDefaultValue() {
return defaultValue;
}
}
Now you can use them like CustomAddonLang.CUSTOMADDON_BLAH_BLAH.sendMessage(player)
.
Important
Don't forget to register your custom messenger class:
@Override
public void onLoad() {
LanguageManager.registerMessenger(CustomAddonLang.class);
}
The first parameter represents the English default value which is used if a translation (even the en.yml English file) doesn't have a value for this language entry.
The numbers at the end are grouping values for defining the path of these messages inside the language files (e.g. en.yml)
The numbers are refer to the position of the nth underscore _
in the enum's name.
- If the position of an underscore is specified at the end, the next word is considered as a nested YAML entry.
- If the position of an underscore is not specified at all, the next word is concatenated to the previous word separated by a hyphen
-
.
Examples:
COMMAND_CUSTOMADDON_CREATE_BLAH("{$s}Creates blah.", 1, 2, 3),
// ^ 1 ^ 2 ^ 3
Corresponds to the following YAML path:
command:
customaddon:
create:
blah: "..."
Another example:
COMMAND_CUSTOMADDON_CREATE_BLAH("{$s}Creates blah.", 1, 3),
// ^ 1 ^ - ^ 3
Corresponds to the following YAML path:
command:
customaddon-create:
blah: "..."
A very very important thing to remember is that you need to call signalDisable() on your onDisable() if you've registered namespace'd values in the plugin. If you don't do this the plugin can't correctly save data.
If you want to register new placeholders to the plugin, you can do so like this, enums are a pretty easy and common way to register placeholders, even tho they'll never be used within the code itself, if you don't like that approach, all you need to do is to register placeholders using KingdomsPlaceholder.of(name, defaultValue, translator) function which ultimately implements KingdomsPlaceholder class and registers it with KingdomsPlaceholder.register(instance)
.
A KingdomEntity
is any entity that is spawned and managed by KingdomX plugin related mechanisms (KingdomsX plugin + addons + other plugins using Kingdoms API) which allows a set of predefined rules to be applied to a certain Entity
that is forced by KingdomEntityManager
class:
- All kingdom entities have a certain target that will not change by the standard Minecraft behavior. It will only change when manually instructed to.
- Keeps track of the amount of damage made by other entities.
- Prevents slimes from splitting into smaller slimes. (
SlimeSplitEvent
) - Prevents them from using any portals. (
EntityPortalEvent
) - Prevents them from picking up items. (
EntityPickupItemEvent
) - Prevents them from dropping any EXP. (
EntityDeathEvent
) - Prevents them from burning. (
EntityCombustEvent
) - Prevents them from being tamed. (
EntityTameEvent
) - Adds nice particle effects on spawn/death (cloud particles)
- Removes these entities when the server is shutdown/restarted.
- Other plugins that support KingdomsX plugin will recognize these entities as custom kingdoms entities.
All champions, kingdom guards, kingdom soldiers (soldier turrets), etc are registered with this mechanism.
In order to register your own basic kingdom entity you can use the following code:
Entity entity = ...;
KingdomEntity kEntity = new KingdomEntity(entity, owningKingdom, target);
KingdomEntityRegistry.addMob(kEntity);
The owningKingdom
argument is the kingdom that this entity belongs to. There are other types of specialized KingdomEntity
such as KingdomChampionEntity
and KingdomLandEntity
that define additional behavior but these are mostly for internal purposes. If you still wish to use them, you can read their documentation for the classes.
Finally if you have a main plugin and you want to make an addon for kingdoms compatibility, you can register the addon as an available downloadable content using new AddonRepository(pluginInstance, name, path, metaFileURL, description).register()
where name
is any displayname and path
is the data node (it's not a file or URL path) and metaFileURL
points to a URL
which must point to an accessible .yml
file containing addon information like this one. A direct URL to that file for example would be new URL("https://raw.githubusercontent.com/CryptoMorin/KingdomsX/master/addons/addon-meta.yml")
If you don't have a main plugin, you can simply ask me to add your addon information to the original addon-meta file, but that'd require me to update it every time a new version is out.
This is a completely optional component, but recommended to implement. Please read Data Corruption first for more info. This adds a way to process data when the command is executed so you can do your own corrections for your own addon.
Additionally, you might want to implement a ChunkEditingHandler
instead if you wish to perform operations on chunk block/entity data.
public final class MyDataCheckup extends CheckupHandler {
private static final MyDataCheckup INSTANCE = new MyDataCheckup();
private static final Messenger MSG = new StaticMessenger("{$c}Something is corrupted! %broken%");
static {
CheckupHandler.addCheckupHandler(INSTANCE);
}
MyDataCheckup() {
// Another way to handle data which only works for Kingdom, Nation, KingdomPlayer, Land and Mail.
// This is simply to prevent looping the data again and using the main loop used by the default FSCK.
addPredefinedHandlers(Kingdom.class, kingdom -> {
if (something == null) ...
});
}
@Override
public void handle(@NotNull CheckupProcessor checkupProcessor) {
KingdomsDataCenter dataCenter = checkupProcessor.getDataCenter();
for (Kingdom kingdom : dataCenter.getKingdomManager().getLoadedData()) { // Everything is already loaded.
if (something == null) {
checkupProcessor.addEntry(MSG, settings -> {
settings.raw("broken", something);
});
}
}
}
}