API - Minecraft-Maxmc/KingdomsX-ChineseTranslation GitHub Wiki

王国的API是经过再三考虑后才编写的. 该API非常稳定且易懂 任何拥有Java基础的人都可以使用该API. 如果有需要, API 会毫不犹豫地被更改, 因此某些插件可能会在API更新后中断.

开始

不要忘记将 Kingdoms 加入到你的 plugin.yml -> softdepend 你需要将 version替换为 Spigot 中的版本, 像这样:

Note: 由于某些构建中的某些本地更改, 您可能会收到有关 XSeries 的一些错误. 因此, 如果出现该错误, 只需排除 XSeries.

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'
}

Gradle Kotlin DSL

repositories {
    mavenCentral()
}

dependencies {
    compileOnly("com.github.cryptomorin:kingdoms:version")
}

手动 你可以从 Discord 服务器上免费下载到该插件.

所有的对象都针对 equalshashCode 方法优化过, 所以这些方法可以使用, 但是 强烈建议只选择特定字段来用来比较. 例如, 永远不要在比较Lands 的时候使用equls, 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())
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.
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.
KingdomResourcePointDonateEvent Called when a player donates resource points to 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 a land for a kingdom.
UnclaimLandEvent When someone unclaims a land for a kingdom. Called even for /k unclaimall
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<?>> When someone interacts with a structure or a turret.
TurretActivateEvent When a turret wants to target an entity.


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. This method is completely async.

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

// or
KingdomsMap.display(player, height, width, kp);

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.

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

A manual way to do this, and get to know how canFight method works.

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 spawned mob:

LivingEntity entity = ...;
boolean isKingdomMob = PvPManager.isKingdomMob(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 = Kingdom.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.getLandFromSession(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();
}
⚠️ **GitHub.com Fallback** ⚠️