API - CryptoMorin/KingdomsX GitHub 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

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 newer versions are 90% not going to be available.

version: 1.16.8.1.1

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.

You might also want to take a look at multi-plugin APIs such as FactionsBridge which supports a variety of group and land management plugins.


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 and JetBrains annotations to help programmers.

Certain methods of some classes begin with the word unsafe which should not be used and are usually for allowing to modify collections or maps that are designed to not be directly modified or changing values while bypassing checks. These methods are for internal usage only and you should absolutely not even call them at all unless you know what you're doing.


The plugin also has some extension functions that eases the process of obtaining kingdom objects and they can all be found in KingdomsBukkitExtensions.kt file. However note that this file also contains other extensions that are just for ease of working with Bukkit objects themselves as well, mostly for UUIDs.

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 equivalent 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();


Processors

Since kingdoms is extremely customizable, it'll become extremely hard to maintain two methods that do the same thing, one that performs actions and interacts with the user (such as playing sounds and sending messages) and one that just returns a boolean for API purposes for actions such as PvPing, building or interacting. A solution is to introduce classes that can do all of these called processors:

Processor Description
ClaimProcessor Used for claiming lands.
UnclaimProcessor Used for unclaiming lands.
BuildingProcessor Used for placing/breaking blocks.
InteractProcessor For use/interact.

These processors implement KingdomsProcessor:

  • process(): Processes the issue and sees if there's any issue.
  • isSuccessful(): Whether there are no issues with performing this action.
  • getIssue(): If there is an issue. This is a Messenger, but should not be used directly. You should use sendIssue() instead as Messengers themselves rely on variables for inside the messages. sendIssue() will take care of those variables which was saved during process().
  • finalizeProcess(): Should be called only if isSucessful() is true. This is when there is no issue with performing an action, but certain things have to be changed based on the plugin configuration. Such as build charges or claiming costs.

E.g. Modifying the ClaimProcessor. There are variety of methods that you can override, in this example we used initialChecks. In kingdoms processor, when a messenger is returned, it indicates an issue and the message is sent to the player that performed the action if any.

public final class CustomClaimProcessor extends ClaimProcessor {
    public static void register() { // Call this on your onEnable()
        ClaimProcessor.setBuilder(CustomClaimProcessor::new);
    }

    protected CustomClaimProcessor(SimpleChunkLocation chunk, KingdomPlayer kp, Kingdom kingdom) {
        super(chunk, kp, kingdom);
    }

    @Override
    protected Messenger initialChecks() {
        if (kingdom.getMembers().size() > 100) {
            return new StaticMessenger("&cKingdoms with more than 100 members cannot claim lands.");
        }
        return null; // There are no issues.
    }
}

There are some unstandard/undocumented processors that differ in how they work:

Processor Description
KingdomsChatProcessor Sending messages to specific channels from certain players.
KingdomItemHologramProcessor Showing holograms to players for KingdomItems.
LanguagePackDownloader Installs/Updates language packs with an advanced fast retrying system.
CheckupProcessor For [/k admin fsck] processors. Check the CheckupProcessor API section.
ConditionProcessor Simple class for processing conditional options with variable handling.
PvPManager.canFight() Returns a boolean if two players can fight or not.


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 (which inherit the KingdomsObject class):

  • KingdomPlayer
  • Land
  • Kingdom
  • Nation
  • Mails
  • KingdomItem

Metadata is simply a map with the key as a Namespace 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.

So as a simple example, to save an int to a kingdom, you can use the following:

// You should preferably only use a single namespace for all your data.
private static final META_NS = new Namespace("MyPlugin", "LEVEL");

Kingdom kingdom = ...;
KingdomMetadataHandler metaInt = new StandardKingdomMetadataHandler(META_NS);
kingdom.getMetadata().put(metaInt, new StandardKingdomMetadata(10));

// Retrieve the value:
StandardKingdomMetadata data = (StandardKingdomMetadata) kingdom.getMetadata().get(META_NS);
int lvl = data.asInt(); // If the data types don't match, an error will be thrown.

A full example of registering a functioning metadata and using it can be found here which uses advanced metadata handlers instead of the simple standard handler. Don't forget to call signalDisable() after registering a custom metadata handler. This is not needed for the standard metadata handler.



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.
GroupRenameEvent When a kingdom/nation is renamed with /k rename or /k nation rename or by other means.
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.
PlayerChangeChatChannelEvent When a player switches channels using /k c command.
GroupServerTaxPayEvent When a group pays server taxes. Not kingdom or nation taxes.
RankCreateEvent, RankDeleteEvent, RankNameChangeEvent, RankColorChangeEvent, RankSymbolChangeEvent, RankPriorityChangeEvent, RankMaxClaimsChangeEvent, RankMaterialChangeEvent These are called when a player changes the ranks through nexus Ranks & Permissions section.

All kingdom events support metadata which uses Namespace. This can be helpful for including arbitrary information inside events for later uses by other plugins.



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 unique namespaced value to avoid conflicts. 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

Note: If you registered a custom namespace'd value to the plugin, make sure that you call Kingdoms.get().signalFullSave() on your onDisable() or if you're using an addon, you can simply call signalDisable() from the class that implemented the Addon interface.



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 KingdomItems

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

TurretStyle turretStyle = TurretRegistry.getStyle("arrow");
StructureStyle structureStyle = StructureRegistry.getStyle("powercell");

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

land.getTurrets().put(location, turret);
land.getStructures().put(location, structure);

turret.spawnHolograms(land.getKingdom());
structure.spawnHolograms(land.getKingdom());

Removing KingdomItems

# This can be aquired using Land#getStructures() or Land#getTurrets()
KingdomItem<?> kingdomItem = ...;

KingdomItemRemoveContext context = new KingdomItemRemoveContext(); # All the properties are optional.
context.setCause(event); # If this was caused by another event
context.setPlayer(kp); # If a player caused this item to be removed.
context.dropsItem(false); # If the item should be dropped after being removed.
context.modifier(Consumer<KingdomItemBreakEvent<?>>) # Usually used for modifying the metadata of the event being firing it.
kingdomItem.remove(context);

KingdomItems from ItemStack

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
}

// Or more easily for players
KingdomPlayer kp;
kp.isInvading()

Getting invasions

// From players:
Player player = ...;
KingdomPlayer kp = KingdomPlayer.getKingdomPlayer(player);
Invasion invasion = kp.getInvasion();

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

Land mainLand = invasion.getOriginLand();

Getting invasion stuff from land.

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

As you can see an invasion session is described by an Invasion class. This class holds all possible information about an invasion. A standard type of invasions are plunder invasions which is handled by Plunder class which extends Invasion.

In order to start an invasion manually, you can use the following code:

KingdomPreInvadeEvent event = InvasionFactory.standardInvasion(land, acceptedLands, player, ransackMode);
if (event.isCancelled()) return;
Invasion invasion = event.getInvasion();

// The KingdomInvadeEvent gets called when the cooldown of standardInvasion is ended.

You can use InvasionFactory to access a variety of different helpful tools regarding invasions since the standardInvasion() method and its more raw version prepareInvasion() method almost perform no checks such as shield, pacifism, invasion costs and etc that the /k invade command does.

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);
    }
}

Reserved

The location API in Kingdoms is a bit complicated. There are many classes to do the same thing with different mutability and datasets. So instead of listing them all here, there's a general formula that can be followed to get the desired class that you want, or just use Location class (not from Bukkit, but Kingdoms) that is accepted anywhere that other location types are accepted.

This is going to be like writing chemical equations, where we have a carbon element that's usually in the middle and others as prefix and suffix. Our main element here is going to Vector or Location. You have to go thru this list in order:

  • Immutable (prefix): All location types that are not intended to be changed (final modifier for xyz) have this prefix.
  • Block: All locations that have an integer int xyz. If this is not specified, the xyz will be of type double
  • Main Element:
    • Vector: This location will only contain xyz data without any world data.
    • Location: This location will contain both xyz and world data.
  • Dimension:
    • 3: This location has xyz coordinates.
    • 2: This location only has xz coordinates. (Not all location types support this)

E.g.

After finding your desired type, they all should support a static of() method that you can get an instance of these locations.

⚠️ **GitHub.com Fallback** ⚠️