API - CryptoMorin/KingdomsX Wiki

Kingdoms API is one of the things that was reconsidered a lot when it was being coded. It's quite stable and easy to learn.
Anyone with basic Java knowledge can use the API.
The API will definitely change if needed without hesitation, so some plugins might break after API updates.

Getting Started

Don't forget to add Kingdoms to your plugin.yml -> softdepend
You need to replace version with the version on Spigot in the following:

The version specified in this page is considered to be the most up to date version with the latest API version that's guaranteed to be on Maven. Other versions are 90% not going to be available.

version: 1.13.9

Maven

<dependency>
    <groupId>com.github.cryptomorin</groupId>
    <artifactId>kingdoms</artifactId>
    <version>version</version>
    <scope>provided</scope>
</dependency>

Gradle

repositories {
    mavenCentral()
}

dependencies {
    compileOnly 'com.github.cryptomorin:kingdoms:version'
}

Manual You can download the plugin for free on the Discord server.

If you're seeing that a beta version is not specified in the version field above, that's because you generally shouldn't add support for any software that's in beta version, including Kingdoms since the API is subject to a lot of changes during that time. Of course this means that you might have to update the support once the version is stable, but that has its own disadvantages. If you wish to do so, then you'll have to temporarily add Kingdoms as a local dependency, which is quite easy with Gradle (it's also possible with Maven, but a little harder). For more information about beta versions, please refer to stability section.


All the objects have optimized equals and hashCode methods that can be used, but it's always recommended to choose the correct data from the object you want to compare. For example, never use equals for Lands, they have optimized methods, but not when you're trying to compare them normally. Refer to Optimization section for more info.

You can figure out most of the things you're looking for just by following your IDE's auto completion. Take a look at some examples at the end of the page.
90% of the API is thread-safe unless the method is dealing with Bukkit related operations. Most of the API is also marked with Nullability Annotations.

Basics

Getting Kingdom Player

You can get a kingdom player by either using an OfflinePlayer or UUID object. They're both the same, none is accurate or performs better than the other.

Player player = ...;
KingdomPlayer kp = KingdomPlayer.getKingdomPlayer(player);

Getting Kingdoms & Nations

You can get a kingdom/nation either by its UUID or name. It's always recommended to use the UUID.

Kingdom kingdom = Kingdom.getKingdom(name/id);
Nation nation = Nation.getNation(name/id);

It's also useful to know that Kingdom and Nation objects both inherit from a class called Group which shares the most common methods for these two objects. These two objects also have some methods that are exactly the same as Group, but with a different name. E.g. Nation#getCapitalId() and Kingdom#getKingId() are equivelant to Group#getOwner()

90% of the time you don't even need to get a kingdom this way, most of the kingdom objects have a way to get to the kingdom object. They're all named getKingdom()
Notice that some objects have getKingdomId() method, this method is highly recommended instead of getKingdom() for performance reasons. For example instead of doing KingdomPlayer#getKingdom().equals(Kingdom) do Kingdom#isMember(Player) or KingdomPlayer#getKingdomId().equals(kingdomId)
Kingdom objects are cached for KingdomPlayer and Land.

KingdomPlayer kp = ...;
if (kp.hasKingdom()) { // A better way is to use kp.getKingdom() and check if it's null. Same for nations.
  Kingdom kingdom = kp.getKingdom();
  if (kingdom.hasNation()) {
     Nation nation = kingdom.getNation();
     // ...
  }
}

Getting Lands, Structures & Turrets

Kingdoms save locations (other than kingdom and nation home locations) in SimpleLocation and SimpleChunkLocation objects for memory performance. You can use the Object#of() method of these objects to get the location for your right object. Using the correct one can improve performance.

  • SimpleLocation is the simplified version of Location
  • SimpleChunkLocation is the simplified version of Chunk

These two objects have most of the useful methods (even more) of Location and Chunk
A land that's null is a land that has absolutely no (kingdom related) data attached to it.
A land that's not null, but is also not claimed (!isClaimed()) doesn't have a kingdom, but it might have protected chests, turrets, structures or other data in it.

Player player = ...;
SimpleChunkLocation chunk = SimpleChunkLocation.of(player.getLocation());
Land land = chunk.getLand();
// or simply
Land land = Land.getLand(player.getLocation());
if (land == null) return;

Structure structure = land.getStructure();
SimpleLocation = simpLocation = SimpleLocation.of(player.getLocation());
Turret turret = land.getTurrets().get(simpLocation);
if (land.isClaimed()) {
  Kingdom kingdom = land.getKingdom();
  // ...
}

Getting All/Top Kingdoms & Nations

Top kingdoms and nations depend on their might.
Make sure to use these methods properly and use them asynchronous when possible.

// Kingdoms
Collection<Kingdom> kingdoms = DataHandler.get().getKingdomManager().getKingdoms();

// Nations
Collection<Nation> nations = DataHandler.get().getNationManager().getNations();

// Lands
Collection<Land> lands = DataHandler.get().getLandManager().getLands();

In this example we get the top 10 kingdoms/nations.

// Kingdoms
List<Kingdom> kingdoms = DataHandler.get().getKingdomManager().getTopKingdoms(0, 10);

// Nations
List<Nation> kingdoms = DataHandler.get().getNationManager().getTopNations(0, 10);

Accessing Protection Signs

Block block = ...;
Optional<ProtectionSign> protectionSign = ProtectionSign.getProtection(block);
if (!protectionSign.isPresent()) return;
ProtectionSign protection = protectionSign.get();
List<UUID> group = protection.getGroup();


Custom Permissions

You can register your own custom permissions for ranks to use! It's really easy.

public enum SomePermission implements KingdomPermission {
    FIRST, SECOND, THIRD;
}

Note: If you change the enum names, the plugin will not recognize the permission and the data will be discarded

Then you'll have to register it with:

try {
    KingdomPermissionFactory.registerPermissions(plugin, SomePermission.values());
} catch (IllegalAccessException e) {
    e.printStackTrace();
}

Note: You cannot register permissions after the server is fully loaded because the plugin loads data after that and if it can't find your permission, it'll discard the data. If you attempt to register permissions after that, you'll get a IllegalAccessError, so a good place to register your permissions is onEnable()
If you attempt to register a permission that already exists or use the kingdoms plugin instance, you'll get a IllegalArgumentException

After you registered your permissions. You can do:

KingdomPlayer kp = ...;
kp.hasPermission(SomePermission.FIRST)
kp.getRank().getPermissions().add(SomePermission.SECOND);

// If other plugins want to use your permission,
// but don't want to import your JAR for some reasons, they can use:
KingdomPermission permission = KingdomPermissionFactory.getPermission("THIRD");


Optimization

Here I'll give you some examples for when and which methods to use for your specific case.

This first example applies to getLand(), getKingdom(), getNation() and etc...

KingdomPlayer kp = ...;

// DONT
if (kp.hasKingdom()) {
    if (kp.getKingdom().hasAttribute(...) && kp.getKingdom().equals(...)) 
}

// DO
Kingdom kingdom = kp.getKingdom();
if (kingdom != null) {
    if (kingdom.hasAttribute(...) && kingdom.equals(...))
}

Checking if a land belongs to a kingdom.
Scenario 1: You have a location (Location, SimpleChunkLocation, SimpleLocation, Block and etc...) and a Kingdom instance.

// Or convert it to SimpleChunkLocation of other types.
SimpleChunkLocation chunk = ...;
Kingdom kingdom = ...;

// DONT
Land land = chunk.getLand();
if (land != null && land.isClaimed && land.getKingdom().equals(kingdom))

// DO
if (kingdom.isClaimed(chunk))

Scenario 2: You have a Land instance and the Kingdom ID.

Land land = ...;
UUID kingdomId = ...;

// DONT
Kingdom kingdom = Kingdom.getKingdom(kingdomId);
if (kingdom.isClaimed(land.getLocation()))
// OR
if (kingdom.getId().equals(land.getKingdomId()))

// DO
if (land.getKingdomId().equals(kingdomID))

Comparing Lands

Land one = ...;
Land two = ...;

// DONT
if (one.equals(two))

// DO
if (one.simpleEquals(two))

Saving data

// DONT
Map<Land, ...> map = new HashMap<>();
Set<Land> set = new HashSet<>();

// DO
Map<SimpleChunkLocation, ...> map = new HashMap<>();
Set<SimpleChunkLocation> set = new HashSet<>();

// DONT
Map<Turret, ...> map = new HashMap<>();
Set<Turret> set = new HashSet<>();

// DO
Map<SimpleLocation, ...> map = new HashMap<>();
Set<SimpleLocation> set = new HashSet<>();

// This applies for structures which need to be saved with their SimpleLocation as well and
// Kingdom and KingdomPlayer which need to saved with their UUID.


Turrets & Structures

Making custom turrets and structures are more complicated because of the abstraction level.
Turrets and Structures have 2 different main objects, 3 for turrets.
When it's mentioned that a class is a another x class it means that class extends x class.

  • KingdomItem: Unlike the name, each turret and structure that's placed is called a kingdom item. They handle data for each turret and structure such as their location, ammo, holograms and etc...
  • KingdomItemType: The main mechanics for the turrets and structures that are the same type. They handle turrets and structures GUIs, how their data is saved and how turrets activate.
  • KingdomItemStyle: The modified version of (extends) KingdomItemType for turrets. They modify how turrets GUI works and how they activate.

KingdomItem class for turrets is called Turret and for structures it's called Structure.
KingdomItemType class for turret types is called TurretType and for structures it's called StructureType

So, each Structure & Turret is a KingdomItem and each KingdomItem has a KingdomItemType.
For turrets, each Turret has a KingdomItemStyle which is technically a KingdomItemType but with modified options which you can find the in turret config.
Structures cannot have a KingdomItemStyle because their mechanics are so unique that there is nothing special about them to be modified.

Let's understand KingdomItemStyle and KingdomItemType a little more.
Go to Plugins/Kingdoms/Turrets folder in your server. You'll see all the turret files. Each of these files are a KingdomItemStyle and their KingdomItemType that they're modifying is the type property inside the file.
Now this means that servers can make their own KingdomItemStyle without needing to code anything. That makes it more easier to understand.

For example, by default there is a turret style named arrow.yml that uses the type arrow in the Turrets folder.
In this case, our KingdomItem is a RangedTurret which is a Turret.
And our KingdomItemStyle is a RangedTurretType which is a TurretType which is a KingdomItemType. Yes, in this case both of our KingdomItemType and KingdomItemStyle are called arrow which is allowed, but they're not the same.

Image Example

As an example for structures, take a look at powercell structure in structures.yml:

  • KingdomItem is a Structure.
  • KingdomItemType is a StructurePowercell which is a StructureType

But now for an Extractor:

  • KingdomItem is an Extractor which is a Structure
  • KingdomItemType is a StructureExtractor which is a StructureType

Now you might ask why extractors have their own kingdom item object but not powercells? Because extractors save data such as the last collected time milliseconds, but powercells don't need to store any extra information.

IMPORTANT: If the turret or structure you're making has additional data (that is not temporary or transient) other than the ones provided in the superclass, you MUST override its KingdomItem hashCode() method in addition to calling its super.hashCode() while building the hash. If you don't, the server might not save your data to the data files.

Example of overriding hashcode methods correctly for a kingdom item like extractors that has extra data:

@Override
public int hashCode() {
    int prime = 31;
    int result = 15;

    result = prime * result + super.hashCode();
    result = prime * result + lastCollector.hashCode();
    result = prime * result + Long.hashCode(lastCollected);
    return result;
}

Extractor Tree

Example

Now let's make a complicated turret.

public class TurretGhasterBlaster extends RangedTurret { // RangedTurret extend Turret and Turret extends KingdomItem<Turret>
    private int blasts = 0;
    private int durability = 1000;
    public GhasterBlasterTurret(KingdomItemStyle type, SimpleLocation location) {
          super(type, location);
    }

    @Override
    public void activate(LivingEntity target, Kingdom kingdom) {
        super.activate(target, kingdom);
        target.setVelocity(new Vector(0, blasts, 0));
        blasts++;
        durability--;
        if (durability == 0) remove(getLand());
    }

    @Override
    public int hashCode() {
        int prime = 31;
        int result = 15;

        result = prime * result + super.hashCode();
        result = prime * result + blasts;
        result = prime * result + durability;
        return result;
    }

    public int getBlasts() { return blasts; }
    public void setBlasts(int blasts) { this.blasts = blasts; }
}

public class TurretGhasterBlaster extends TurretTypeRanged { // TurretTypeRanged extend TurretType and TurretType extends KingdomItemType<Turret>
    public TurretGhasterBlaster () {
        super("ghasterblaster", false, true);
    }

    @Override
    public boolean activate(TurretActivateEvent event) { ... }

    @Override
    public void serialize(JsonObject json, Turret turret, Type type, JsonSerializationContext context) {
        GhasterBlaster rangedTurret = (GhasterBlaster) turret;
        super.serialize(json, turret, type, context);
        json.addProperty("ammo", rangedTurret.getAmmo());
        json.addProperty("blasts", rangedTurret.getBlasts());        
    }

    @Override
    public Turret deserialize(KingdomItemStyle style, JsonObject json, Type type, JsonDeserializationContext context) throws JsonParseException {
        GhasterBlaster turret = new GhasterBlaster(style, null);
        turret.setLevel(json.get("level").getAsInt());
        turret.setAmmo(json.get("ammo").getAsInt());
        turret.setBlasts(json.get("blasts").getAsInt());
        turret.setHolograms(context.deserialize(json.get("holograms"), new TypeToken<List<UUID>>() {
        }.getType()));
        return turret;
    }

    @Override
    public Turret build(KingdomItemStyle style, SimpleLocation location, KingdomPlayer kp, NBTWrappers.NBTTagCompound tag) {
        Turret turret = new GhasterBlaster(style, location);
        if (tag != null) turret.setData(tag);
        return turret;
    }

Now let's register our turret type.

register(new TurretGhasterBlaster());

Our style is automatically registered.


Now in our Turrets folder let's add blaster.yml file

name: "&0Ghaster&cBlaster"
type: ghasterblaster
cost: 300
max-level: 3
hologram:
  1:
    lines:
      - "&8-=[ &0Ghaster&cBlaster &8]=-"
    height: 1
placing:
  whitelist: true
  blocks:
    - CONTAINS:FENCE
    - CONTAINS:WALL
range: 7 + lvl
cooldown: 8 - lvl
max-targets: min(2, 1 + (lvl + 1))
max-ammo: lvl * 2000
upgrade-cost: lvl * 80
fire: 1
particle: ~
effects:
  1: []
projectile:
  1: FIREBALL
speed: 1 + (lvl * 0.8)
damage: lvl * 4

block:
  1: PLAYER_HEAD
skull:
  1: "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNmNlZGVjMDRkMjM4MGNkNzcwMjdmOWQ0NDQ1NWM5OGI3ZWRjNWY2NjRjYTBkZDMwYTYxMDY5MDM5MTUzOTFkYiJ9fX0="
  2: "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYmQzNzI4NTc5MzEzMWVkNzU1ZjFiMDA5OGYyOWRkNDEzZDY3NjU2YjYyMDg3Mjg5MzU0OTJiNDliMWQwZDRiYSJ9fX0="
item:
  1:
    name: "&0Ghaster&cBlaster Turret"
    material: FIRE_CHARGE
    lore:
      - "&6A similar turret as flame"
      - "turret but really fast with multiple targets."
      - ""
      - "&9Attributes&8:"
      - "&7⚫ &2Level&8: &6%level_roman%"
      - "&7⚫ &2Ammo&8: &6%ammo%"
      - "&7⚫ &2Range&8: &6%range%"
      - "&7⚫ &2Max Targets&8: &6%max_targets%"
      - "&7⚫ &2Max Ammo&8: &6%max_ammo%"
      - "&7⚫ &2Cooldown&8: &6%cooldown%"
      - "&7⚫ &2Speed&8: &6%speed%"
      - "&7⚫ &2Damage&8: &6%damage%"


Metadata

It's similar to Bukkit's metadata except it's specifically made for kingdoms and stays after restarts.
All the main kingdom objects support metadata:

  • KingdomPlayer
  • Land
  • Kingdom
  • Nation

Metadata is simply a map with the key as a string and the value as an object.
To modify this map, use getMetadata() method. Some custom data objects might not be stored correctly. Please check your data once to see if the data is stored the way you expected or it might lead to some data corruptions.



Events

Event Description
KingdomCreateEvent Called when a kingdom is created via test /k create (Kingdom(UUID id, UUID king) constructor)
KingdomDisbandEvent Called when a kingdom is disbanded either from /k disband, taxes or inactivities. (Kingdom#disband())
KingdomKingChangeEvent When the king of the kingdom changes.
PlayerRankChangeEvent When a player is demoted/promoted.
KingdomInviteEvent When a player invites another player to the kingdom.
KingdomRelationshipChangeEvent Called when a kingdom relationship changes from commands. (Kingdom#acceptRelationShipRequest(...)
KingdomSetHomeEvent Called when a player sets/changes their kingdom home (Kingdom#setHome())
OpenProtectedBlockEvent Called when a player opens a protected block using protection signs.
ChampionAbilityEvent Called when a champion uses an ability.
KingdomInvadeAttackEvent Called when either the champion or the player attack each other.
KingdomInvadeEndEvent Called when an invasion ends with the invasion results and reason.
KingdomPreInvadeEvent Called before the cooldown of /k invade starts.
KingdomInvadeEvent Called after the cooldown of /k invade command.
SiegeCannonHitEvent When a Siege Cannon's projectile hits a land. You can modify its shield damage.
KingdomJoinEvent Called when someone join a kingdom. This is also called right before KingdomCreateEvent when someone creates a kingdom.
KingdomLeaveEvent Called when someone leaves a kingdom.
GroupResourcePointConvertEvent Called when a player donates resource points to a nation or a kingdom. Specifically called when KingdomPlayer#donate is used.
LandChangeEvent Called when someone goes from a land to another land. This is basically a chunk change event but with kingdoms data. (for performance reason.
LandLoadEvent When a kingdom land data loads. (When chunks load) Note that this will not do anything if the land is null (with no data attached to it)
LandUnloadEvent When a kingdom land data unloads. (When chunks unload) Note that this will not do anything if the land is null (with no data attached
ClaimLandEvent When someone claims one or more lands for a kingdom in one or different worlds.
UnclaimLandEvent When someone unclaims one or more lands for a kingdom in one or different worlds. Called even for /k unclaim all
NexusMoveEvent After someone used /k nexus to move their nexus.
KingdomItemBreakEvent<? extends KingdomItem<?>> When someone breaks a structure or a turret block either from the GUI or manually.
KingdomItemPlaceEvent<? extends KingdomItem<?>> When someone places a structure or a turret.
KingdomItemInteractEvent<? extends KingdomItem<?>> A player right-clicks on a structure or a turret to open its GUI.
TurretActivateEvent When a turret wants to target an entity.
GroupRelationshipChangeEvent When a nation/kingdom changes their relationship with another nation/kingdom.
GroupHomeTeleportEvent When a player teleports to nation/kingdom home.
KingdomColorChangeEvent When Group#setColor is called. Which is usually when a player changes it from their nexus settings.
KingdomFlagChangeEvent When Group#setFlag is called. Which is usually when a player changes it from their nexus settings.
KingdomGUIOpenEvent When the plugin is about to open a GUI for the player (before it's opened). You can modify the GUI.
MassWarStartEvent When Mass War event starts.
MassWarEndEvent When Mass War event ends.
KingdomMiscUpgradeUpgradeEvent When a misc-upgrade is upgraded.
KingdomMiscUpgradeToggleEvent When a misc-upgrade is enabled/disabled.
KingdomChampionUpgradeUpgradeEvent When a champion upgrade is upgraded.
KingdomPowerupUpgradeEvent When a powerup is upgraded.
KingdomItemUpgradeEvent When a structure/turret is upgraded.
RankPermissionChangeEvent When a player changes the permissions of a rank through Nexus GUI.
ExtractorCollectEvent When a player collects resource points of an extractor by opening its GUI or using /k extractor command.
OutpostPurchaseItemEvent When a player purchases an item from an Outpost structure.
WarpPadTeleportEvent When a player teleports to another structure using Warp Pad structure.
KingdomPacifismStateChangeEvent When a player changes the pacifism state of the kingdom through nexus.
GroupShieldPurchaseEvent When a player purchases a shield for their kingdom through nexus settings.
MailSendEvent When a player sends a mail to another kingdom or replies to another player.
RankCreateEvent, RankDeleteEvent, RankNameChangeEvent, RankColorChangeEvent, RankSymbolChangeEvent, RankPriorityChangeEvent, RankMaxClaimsChangeEvent, RankMaterialChangeEvent These are called when a player changes the ranks through nexus Ranks & Permissions section.


Registry

Kingdoms allows you to define your own kingdom permissions, relationship attributes and audit logs. All these are done through registries which work with Namespace class.

Available Registries

Registry Object Description
Kingdoms#getPermissionRegistery() KingdomPermission Permissions are for kingdom/nation ranks which the admins of the kingdom can change in their Nexus GUI.
Kingdoms#getRelationAttributeRegistry() RelationAttribute Attributes are agreements/permissions between two kingdoms/nations
Kingdoms#getAuditLogRegistry() AuditLog & AuditLogProvider A record of changes and actions in a kingdom/nation.

Namespace

To register your own namespaced value, you have to register them in your plugin's onLoad() method. Doing so in onEnable will not work. You also cannot unregister them since it'll cause data inconsistencies due to performance reasons.

In order to register your value, you have to make your own unqiue namespaced value to avoid conficts. A namespace can be created like:

Namespace ns = new Namespace("PluginName", "TEST");

The first parameter PluginName can only contain alphabets (no spaces, numbers, underscores, dashes and etc). And the second parameter must be an all-caps alphanumeric (including underlines) value.

KingdomPermission

public static final KingdomPermission PERMISSION_INV_TAKE = new KingdomPermission(new Namespace("PluginName", "INV_TAKE"));

@Override
public void onLoad() {
  Kingdoms.get().getPermissionRegistery().register(PERMISSION_INV_TAKE);
}

@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onInventoryTake(InventoryClickEvent event) {
  Player player = (Player) event.getWhoClicked();
  KingdomPlayer kp = KingdomPlayer.getKingdomPlayer(player);
  SimpleChunkLocation chunk = SimpleChunkLocation.of(player.getLocation);
  Kingdom kingdom = kp.getKingdom();
  
  if (kingdom == null || land == null || !kingdom.isClaimed(chunk)) return;
  if (!kp.hasPermission(PERMISSION_INV_TAKE)) event.setCancelled(true);
}

RelationAttribute

public static final RelationAttribute ATTR_SHOOT_IN_LAND = new RelationAttribute(new Namespace("PluginName", "SHOOT"));

@Override
public void onLoad() {
  Kingdoms.get().getRelationAttributeRegistry().register(ATTR_SHOOT_IN_LAND);
}

@EventHandler(ignoreCancelled = true)
public void onPlayerShoot(ProjectileLaunchEvent event) {
    Projectile projectile = event.getEntity();
    if (!(projectile.getShooter() instanceof Player)) return;
    
    Player shooter = (Player) projectile.getShooter();
    KingdomPlayer kp = KingdomPlayer.getKingdomPlayer(shooter);
    Kingdom kpKingdom = kp.getKingdom();
    Land land = Land.getLand(shooter.getLocation());
    Kingdom landKingdom = land == null || !land.isClaimed() ? null : land.getKingdom();
    
    if (!ATTR_SHOOT_IN_LAND.hasAttribute(kpKingdom, landKingdom)) event.setCancelled(true);
}

AuditLog

The following example show very simple cases on how to use these logs, however in some cases where it's possible to gather bulk information, it's recommended to make your own delayed log system to gather information for a certain type of action that a player does. For example in below, you could make a log collector class and wait for 10 seconds every time the player enters a new chunk that belongs to its kingdom and if there were no new logs for 10 seconds, add all the chunks that the player has visited in a single list (or set if you want to remove duplicates) this way you're improving the user experience, performance, storage and RAM.

public final class LogLandChange extends AuditLog {
    private SimpleChunkLocation from, to;
    
    protected LogLandChange() {}
    
    public LogLandChange(SimpleChunkLocation from, SimpleChunkLocation to) {
        this.from = from;
        this.to = to;
    }
    
    private static final Namespace NS = new Namespace("PluginName", "LAND_CHANGE");
    public static final AuditLogProvider PROVIDER = new AuditLogProvider() {
        @Override
        public AuditLog construct() {
            return new LogLandChange();
        }
        @Override
        public Namespace getNamespace() {
            return NS;
        }
    };
    @Override
    public AuditLogProvider getProvider() {
        return PROVIDER;
    }

    @Override
    public void deserialize(DeserializationContext context) {
        super.deserialize(context);
        JsonObject json = context.getJson();
        this.from = SimpleChunkLocation.fromString(json.get("from").getAsString());
        this.to = SimpleChunkLocation.fromString(json.get("to").getAsString());
    }

    @Override
    public void serialize(SerializationContext context) {
        super.serialize(context);
        JsonObject json = context.getJson();
        json.addProperty("from", from.toString());
        json.addProperty("to", to.toString());
    }

    @Override
    protected void addEdits(MessageBuilder builder) {
        super.addEdits(builder);
        builder.parse("from", "&2" + + from.getWorld() + ", " + from.getX() + ", " + from.getZ());
        
        // Or using kingdoms built-in language entries:
        builder.parse("to", KingdomsLang.LOCATIONS_CHUNK.parse(LocationUtils.getChunkEdits(to)));
    }
}

@Override
public void onLoad() {
  Kingdoms.get().getAuditLogRegistry().register(LogLandChange.PROVIDER);
}

@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onLandChange(LandChangeEvent event) {
    KingdomPlayer kp = event.getKingdomPlayer();
    Kingdom kingdom = kp.getKingdom();
    if (kingdom == null) return;
    if (kingdom.isClaimed(event.getFromChunk()) || kingdom.isClaimed(event.getToChunk())) {
        kingdom.log(new LogLandChange(event.getFromChunk(), event.getToChunk()));
    }
}

Configs

Now there's a high chance server owners might want to use your registered values in their configs. The way these values are used in configs are a little different. They're used in namespace:value format where namespace is untouched however value is converted into an all lowercase word with underscores replaced with dashes. Also since colons have special meanings in yaml, they have to use one of the string literals.

The config entry corresponds to the Namespace#getConfigOptionName() method.

From previous examples:

Registry Namespace Config Name Relevant Configs
KingdomPermission new Namespace("PluginName", "INV_TAKE") PluginName:inv-take ranks.yml
RelationAttribute new Namespace("PluginName", "SHOOT") PluginName:shoot relations.yml
AuditLog new Namespace("PluginName", "LAND_CHANGE") PluginName:land-change logs.yml GUI


Examples

We're going to take a look at some examples that are only a little harder to find for developers.

Basic

Getting all the kingdom allies.

List<Kingdom> allies = damagerKingdom.getKingdomsWithRelation(KingdomRelation.ALLY);

Checking if player has permission in kingdom or nation.

Player player = ...;
KingdomPlayer kp = new KingdomPlayer(player);
if (kp.hasKingdom()) {
    kp.hasPermission(DefaultKingdomPermission.HOME);
    if (kp.getKingdom().hasNation()) kp.hasNationPermission(DefaultKingdomPermission.HOME); // Spawn
}

Joining a kingdom.

Player player = ...;
KingdomPlayer kp = KingdomPlayer.getKingdomPlayer(player);
Kingdom kingdom = Kingdom.getKingdom(...);
kp.joinKingdom(kingdom);

Opening /k map for players.

Player player = ...;
KingdomPlayer kp = KingdomPlayer.getKingdomPlayer(player);
kp.buildMap().display(); // This considers the custom map size set by the player.

// or
new KingdomsMap().forPlayer(player).display();

Checking if two players can fight

If you're using EntityDamageByEntityEvent event, simply check if the event is cancelled. Kingdoms uses LOW event priority to make it easier for other plugins.

To check if two players can PvP each other in any circumstances you can use the following code. This will consider all the kingdom features and configured options.
Note: It's important that which player is the damager and which is the victim (method arguments order).

Player damager = ...;
Player victim = ...;
boolean canFight = PvPManager.canFight(damager, victim);

An oversimplified manual way to do this:

Player damager = ...;
Player victim = ...;

KingdomPlayer damagerKp = KingdomPlayer.getKingdomPlayer(damager);
if (damagerKp.isAdmin() || !damagerKp.hasKingdom()) return true;
KingdomPlayer victimKp = KingdomPlayer.getKingdomPlayer(victim);
if (!victimKp.hasKingdom()) return true;

if (damagerKp.isPvp() && victimKp.isPvp()) return true;
Kingdom damagerKingdom = damagerKp.getKingdom();
Kingdom victimKingdom = victimKp.getKingdom();

return !victimKingdom.hasAttribute(damagerKingdom, KingdomRelation.Attribute.CEASEFIRE);

Checking if an entity is a kingdom entity. A kingdom entity is any entity that is spawned by Kingdoms plugin.
This includes champions, turret soldiers, guards and etc.

LivingEntity entity = ...;
boolean isKingdomMob = KingdomEntityRegistry.isKingdomEntity(entity);

Claiming a Land

Player player = ...;
Location location = player.getLocation();
KingdomPlayer kp = KingdomPlayer.getKingdomPlayer(player);
if (!kp.hasKingdom()) return;

// WorldGuard Support
if (ServiceHandler.isLocationInRegion(loc)) return;
SimpleChunkLocation simpLocation = new SimpleChunkLocation(loc);
Land land = Land.getLand(location);
if (land != null && !land.isClaimed()) kp.getKingdom().claim(kp, simpLocation);

Resource Points Donation

You don't need to use everything from here. Take what you need.

Collection<ItemStack> items = ...;
List<ItemStack> stacked = XItemStack.stack(items);
Player player = ...;
KingdomPlayer kp = KingdomPlayer.getKingdomPlayer(player);

// You can leave the second argument as null if you don't want to modify anything.
Pair<Long, List<ItemStack>> result = ResourcePointManager.convertToResourcePoints(stacked, (item, leftOvers) -> {
    // Return null if you want the item to be handled normally according to the config.
    // You can also add a part of the item to the leftOvers list. You don't need to clone the item.
    // If you return a nonnull value, the item will not be added to the leftOvers list and the amount will be added to the result.
});
long added = result.getKey();
List<ItemStack> leftOvers = result.getValue();
KingdomResourcePointDonateEvent event = kp.donate(added, stacked, leftOvers);

if (!event.isCancelled()) {
    added = event.getAmount();
    leftOvers = event.getLeftOvers();

    kingdom.addResourcePoints(added);
    if (leftOvers != null) XItemStack.giveOrDrop(player, true, leftOvers.toArray(new ItemStack[0]));
}

Taxes

OfflinePlayer player = ...;
KingdomPlayer kp = KingdomPlayer.getKingdomPlayer(player);
Kingdom kingdom = kp.getKingdom();

Pair<Boolean, Double> result = kingdom.payTaxes(player);
result.getKey(); // Could pay?
result.getValue(); // Tax amount the player paid.

if (!kingdom.hasNation()) return;
Nation nation = kingdom.getNation();
nation.payTaxes(kingdom);

Placing Turrets

Same process for structures.

Block block = ...;
SimpleLocation location = new SimpleLocation(block);

// You can leave the kp and nbt data as null if you don't have it.
Land land = location.toSimpleChunkLocation().getLand();
Turret turret = type.getType().build(type, location, kp, nbt);

land.getTurrets().put(location, turret);
turret.spawnHologram(land.getKingdom());

Kingdom Items

ItemStack item = ...;
NBTWrappers.NBTTagCompound nbt = ItemNBT.getTag(item);
nbt = nbt.getCompound(Kingdoms.NBT);

// It's not a kingdom item!
if (nbt == null) return;
String tag = nbt.get(StructureType.METADATA, NBTType.STRING);
// It's a structure.
if (tag != null) return;

tag = nbt.get(TurretType.METADATA, NBTType.STRING);
if (tag != null) return;// It's a turret.
// If it ever passed the above check then I don't know what the hell you did to the item.

Invasions

Checking if a player is invading or an entity is a champion.

Entity entity = ...;
if (entity.hasMetadata(Invasion.METADATA)) {
   if (entity instanceof Player) // invader
   else // champion
}

Getting the land under attack.

// From players:
Player player = ...;
KingdomPlayer kp = KingdomPlayer.getKingdomPlayer(player);
Land land = kp.getInvading();

// From entity (champions):
Entity entity = ...;
Land land = Invasion.getInvasion(entity);

Getting invasion stuff from land.

Land land = ...;
Invasion invasion = land.getInvasion();
if (invasion.getChampion().isValid()) invasion.end(KingdomInvadeEndEvent.InvasionResult.TIMES_UP);

Champion Abilities

Replacing an ability for champions (you cannot create a new one)

public class ChampionAbilityBoom extends ChampionAbility implements Listener {
    public ChampionAbilityResistance() {
        super(ChampionUpgrade.THOR);
    }

    @EventHandler
    public void onDamage(KingdomInvadeAttackEvent event) {
        if (cantUse(event.getInvasion().getDefender().getKingdom())) return;
        if (event.isAttackerChampion()) return;
        if (!MathUtils.hasChance(10) return;
        if (callEvent(event.getInvasion())) return;

        Entity champion = event.getEntity();
        Player player = event.getInvasion().getInvader().getPlayer();
        // A much better way of getting the player is:
        player = (Player) event.getDamager();
        player.getWorld().createExplosion(player.getLocation(), 3f);
    }
}
new ChampionAbilityBoom();

Checking Relation Attributes

Checking relation attributes between two kingdoms needs to follow an order.

Kingdom defender = ...;
Kingdom attacker = ...;
boolean hasAttribute = defender.hasAttribute(attacker, ...);

This code will check the relation attributes for attacker, so for example if defender disabled CEASEFIRE attribute for allies and attacker is allies with defender, hasAttribute will return false. Since the attributes are checked from attacker and only defender changed that attribute. Now it'll return true the other way around. If defender is the attacker, and the attacker is the defender. (it doesn't mean that defender and attacker instances switch, we just switch the order that we check their attribute):

boolean hasAttribute = attacker.hasAttribute(defender, ...);

Since the order matters, sometimes you'll have to do a few extra checks before using the kingdom objects. To make it easier and safer, you can use the following method:

KingdomRelation.Attribute attribute = ...;
boolean hasAttribute = attribute.hasAttribute(kingdom, otherKingdom);

KingdomItemEvent

Listening to KingdomItemEvents is a little special since they use generic types.
In this example, we're going to use KingdomItemPlaceEvent

// If you want to listen to either structures or turrets.
@EventHandler
public void onPlace(KingdomItemPlaceEvent<?> event) {
    if (event.getKingdomItem() instanceof Structure) {
        Structure structure = (Structure) event.getKingdomItem(); 
    }
    if (event.getKingdomItem() instanceof Turret) {
        Turret structure = (Turret) event.getKingdomItem();
    }
}

// Listening to only one type of kingdom item. In this example, structures.
@EventHandler
public void onPlace(KingdomItemPlaceEvent<Structure> event) {
    if (!(event.getKingdomItem() instanceof Structure)) return; // Yes, this line is still necessary.
    Structure structure = event.getKingdomItem();
}

KingdomGUIOpenEvent

In this example, I modified arrow turret style to only be purchasable by opped players.

@EventHandler(ignoreCancelled = true)
public void onNexusOpen(KingdomGUIOpenEvent event) {
    InteractiveGUI gui = event.getGUI();

    // Make sure this is the turret's GUI that's being opened.
    if (!gui.is(KingdomsGUI.STRUCTURES_NEXUS_TURRETS)) return;

    // getOption() returns an option that has been already pushed to the GUI.
    Optional<GUIOption> optionOpt = gui.getOption("arrow");
    if (!optionOpt.isPresent()) return;

    GUIOption option = optionOpt.get();
    Runnable fn = option.getRunnables().values().iterator().next();

    Runnable newFn = () -> {
        if (!event.getPlayer().isOp()) {
            event.getPlayer().sendMessage("You need to be op to buy this turret.");
            return;
        }

        // This preserves the original functionality of the option so you won't need to copy the entire functionality yourself.
        fn.run();
    };

    // toArray() is necessary to prevent concurrent modification.
    for (ClickType clickType : option.getRunnables().keySet().toArray(new ClickType[0])) {
        // The plugin usually has a single functionality for all clicking types.
        option.getRunnables().put(clickType, newFn);
    }
}
⚠️ **GitHub.com Fallback** ⚠️