-
Notifications
You must be signed in to change notification settings - Fork 0
ability template.java
Valor edited this page Nov 9, 2025
·
6 revisions
This page explains how to create, install and use custom abilities for rares in RareSpawns. It is written for server operators and creators who will place Java source files in the plugin data folder — no build tools required if you follow the runtime-compiler approach included with RareSpawns.
Table of Contents
- Quick summary
- Important constraints / requirements
- Ability metadata: @AbilityInfo
- Where to put files
- How to name the class/file and formatting rules
- Minimal example (copy-paste into plugins/RareSpawns/abilities/MyAbility.java)
- How the file is compiled and loaded
- Referencing your ability from entity files
- Testing your ability
- Advanced usage & external dependencies
- Security & best practices
- Examples of custom abilities
- Troubleshooting checklist
- Write a Java class that implements the RareSpawns Ability interface.
- Put the .java file in the plugin data folder: plugins/RareSpawns/abilities/ (i.e. /plugins/RareSpawns/abilities/).
- The file name (without .java) is the ability ID used in entity configs.
- Restart or reload RareSpawns (or run the plugin's ability reload) to compile and load the ability.
- Reference the ability ID in the entity's abilities list (optionally add a per-use cooldown).
- Server must run with a JDK "Java Development Kit" (not a JRE "Java Runtime Environment") because abilities are compiled at runtime using the Java compiler API. If the compiler is unavailable you will see errors in the console.
- The runtime compiler will prepend imports for common Bukkit/RareSpawns classes, so your source file should NOT include a package declaration — places it in the default package.
- Class name must exactly match the filename (case-sensitive).
- The class must:
- implement valorless.rarespawns.ability.Ability
- have a public no-argument constructor (default constructor is fine)
- be annotated with @AbilityInfo (see below) to provide metadata used by the plugin UI/logic
- Ability ID (used in configs) = the filename without .java.
- Every ability should include the AbilityInfo annotation at the top of the class. This provides metadata the plugin reads at runtime.
- Fields (all are strings in the annotation):
- name — display name for the ability
- cooldown — default cooldown in seconds (stored as string but parsed to a number)
- single — "true" or "false" to indicate whether the ability is single-use
- Example: @AbilityInfo(name = "Mother’s Brood", cooldown = "10", single = "false")
- Path: /plugins/RareSpawns/abilities/
- e.g. /home/minecraft/server/plugins/RareSpawns/abilities/MotherBrood.java
- File name (without extension) = ability id used in configs (e.g. MotherBrood.java -> ability id "MotherBrood").
- Class name inside the file must match the file name.
- Do not include a package statement (the runtime compiler prepends imports for you).
- The file should import nothing (not necessary) — the compiler will inject common imports automatically.
- Implement the single method: void execute(valorless.rarespawns.datamodels.EntityData data, org.bukkit.entity.Mob rare)
import valorless.rarespawns.ability.Ability;
import valorless.rarespawns.ability.AbilityInfo;
import valorless.rarespawns.datamodels.EntityData;
import org.bukkit.entity.Mob;
import org.bukkit.entity.Player;
import org.bukkit.entity.Entity;
import org.bukkit.Location;
import org.bukkit.Bukkit;
/**
* Example ability that pings nearby players when the rare executes the ability, every 30 seconds.
*/
@AbilityInfo(name = "Announce Presence", cooldown = "30", single = "false")
public class MyAbility implements Ability {
@Override
public void execute(EntityData data, Mob rare) {
// Simple example: send a message to nearby players
for (Player p : valorless.rarespawns.api.EntityUtils.getPlayersInRadius(rare, 20.0)) {
p.sendMessage("A " + data.customName + " stirs nearby!");
}
// You can use 'rare' to get location, world, target, etc.
Location loc = rare.getLocation();
rare.getWorld().playSound(loc, org.bukkit.Sound.ENTITY_ENDER_DRAGON_GROWL, 1.0f, 1.0f);
}
}- RareSpawns will compile .java files it finds in the abilities folder using the system Java compiler and load them with a dynamic class loader.
- Temporary source files are written to the plugin data folder (compiled/). Compiled .class files are kept under plugins/RareSpawns/abilities/compiled/.
- The plugin provides a reload mechanism (it calls AbilityCreator.reload internally) that compiles and registers abilities asynchronously. You can also restart the server.
- If compilation fails you will see detailed errors in the server console.
- Use the ability ID (filename without .java) in the entity config:
abilities:
- MyAbility # uses the default cooldown defined in @AbilityInfo
- MyAbility:45 # override cooldown to 45 seconds for this entity- The entity config also supports the simple abilityName:cooldown syntax described above.
- Save the .java file into plugins/RareSpawns/abilities/
- Restart the server or run the RareSpawns reload command (or use your existing admin reload tool).
- Watch console logs — you should see lines like "Loading and compiling abilities.." and "Loaded X abilities".
- Spawn the rare (or use the plugin's spawn/command) and verify the ability effects occur.
- If you get compilation errors, check the console stacktrace. Common causes:
- Running without a JDK (no system compiler available)
- Syntax errors in the Java source
- Class name mismatch with file name
- Package statements present (remove them)
- If your ability depends on other plugin classes (for example, you want to call a third-party API), you must make that JAR available on the compiler classpath.
- The plugin includes an advanced.yml setting to add classpath entries (plugins/ subpaths). To use it:
- Edit plugins/RareSpawns/advanced.yml and set i-understand: true
- Add relative paths under compiler.classpath to the JAR(s) you want (paths are resolved relative to the server's plugins directory)
- Restart the server
- Warning: adding arbitrary JARs can introduce compatibility or security issues. Only add trusted plugins and libraries.
For more information, please refer to Compiler Settings.
- Abilities are arbitrary Java code executed on your server — DO NOT run untrusted code.
- Keep backups of your world and plugin data before testing new abilities; if they contain code that edits the world or plugin data.
- Prefer simple, tested logic and avoid long-running or blocking operations in execute(). If you must do heavy work, schedule asynchronous tasks with Bukkit's scheduler.
- Test abilities on a staging server before using them on production.
spawnbrood.java
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.attribute.Attribute;
import org.bukkit.entity.Mob;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Spider;
import valorless.rarespawns.ability.Ability;
import valorless.rarespawns.ability.AbilityInfo;
import valorless.rarespawns.api.EntityUtils;
import valorless.rarespawns.datamodels.EntityData;
import valorless.rarespawns.persistentdatacontainer.PDC;
/**
* Ability: Mother’s Brood
* Spawns a number of spiders around the rare entity and sets their target.
* Creates particle and sound effects at each spawn location.
* <p>
* This ability is used to summon a brood of spiders to attack the rare entity's target.
*/
@AbilityInfo(
name = "Mother’s Brood",
cooldown = "10",
single = "false"
)
public class spawnbrood implements Ability {
/**
* Number of spiders to spawn.
*/
int spiderCount = 5;
/**
* Distance around the rare entity to spawn the spiders.
*/
double spawnRadius = 3.0;
/**
* Scale value for the spiders.
*/
float spiderScale = 0.5F;
/**
* Executes the Mother’s Brood ability, spawning spiders around the rare entity.
* Each spider is set to be aware, targets the rare entity's target, and is tagged as a minion.
* Particle and sound effects are played at each spawn location.
*
* @param data The EntityData for the rare entity.
* @param rare The Mob instance representing the rare entity.
*/
@SuppressWarnings("deprecation")
@Override
public void execute(EntityData data, Mob rare) {
World world = rare.getWorld(); // Get the world the entity is in
for(Location sp : EntityUtils.generateSafeCircularSpawnLocations(rare.getLocation(), spiderCount, spawnRadius)) {
// Spawn a spider at the calculated location
Spider spider = (Spider) world.spawnEntity(sp, EntityType.SPIDER);
spider.setAware(true);
spider.setTarget(rare.getTarget());
spider.setCustomName("Spawn of Nightweaver");
spider.setCustomNameVisible(false);
// Tag the spider as a minion, so no loot is dropped on death
PDC.SetString(spider, "RareEntity-Minion", "true");
// Set the SCALE attribute of the spider
Attribute attri = null;
try {
attri = Attribute.valueOf("SCALE");
}catch(Exception e) {
try {
attri = Attribute.valueOf("GENERIC_SCALE");
}catch(Exception E) {
e.printStackTrace();
}
}
spider.getAttribute(attri).setBaseValue(spiderScale);
// Create a "poof" effect at the spawn location
world.spawnParticle(Particle.CLOUD, sp, 20, 0.2, 0.2, 0.2, 0.01);
world.spawnParticle(Particle.SMOKE, sp, 10, 0.1, 0.1, 0.1, 0.01);
world.playSound(sp, Sound.ENTITY_SPIDER_AMBIENT, 1.0F, 1.0F);
}
}
}curseundead.java
import java.util.HashMap;
import java.util.List;
import org.bukkit.entity.Mob;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import valorless.rarespawns.ability.Ability;
import valorless.rarespawns.ability.AbilityInfo;
import valorless.rarespawns.api.EntityUtils;
import valorless.rarespawns.datamodels.EntityData;
/**
* Ability: Curse of Undead
* Applies a slowness effect to nearby players and sets them on fire if exposed to sunlight during the day.
* <p>
* This ability is used to curse players near the rare entity, making them slower and vulnerable to burning.
*/
@AbilityInfo(
name = "Curse of Undead",
cooldown = "10",
single = "false"
)
public class curseundead implements Ability {
/**
* The radius within which players are affected by the curse.
*/
Double radius = 10.0;
/**
* The potion effects to apply to players (effect type -> level).
*/
HashMap<PotionEffectType, Integer> effects = new HashMap<>();
/**
* The duration of the potion effect in ticks.
*/
Integer effectDuration = 80;
/**
* The duration to set players on fire in ticks if exposed to sunlight.
*/
Integer burnDuration = 100;
/**
* Executes the Curse of Undead ability, applying effects and burning players in sunlight.
*
* @param data The EntityData for the rare entity.
* @param rare The Mob instance representing the rare entity.
*/
@Override
public void execute(EntityData data, Mob rare) {
PrepareEffects();
// Get all players within the specified radius
List<Player> nearby = EntityUtils.getPlayersInRadius(rare, radius);
for(Player player : nearby) {
// Apply all configured potion effects
for(PotionEffectType effect : effects.keySet()){
player.addPotionEffect(new PotionEffect(effect, effectDuration, effects.get(effect) - 1));
}
// If it's daytime and the player is exposed to sunlight, set them on fire
if (player.getWorld().getTime() >= 0 && player.getWorld().getTime() <= 12000) {
if(EntityUtils.isExposedToSunlight(player)) {
player.setFireTicks(burnDuration);
}
}
}
}
/**
* Prepares the potion effects to apply to players.
* Adds slowness level 1 to the effects map.
*/
private void PrepareEffects() {
// PotionEffect - Level
effects.put(PotionEffectType.SLOWNESS, 1);
}
}hardenediron.java
import org.bukkit.Bukkit;
import org.bukkit.entity.Mob;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import valorless.rarespawns.Main;
import valorless.rarespawns.ability.Ability;
import valorless.rarespawns.ability.AbilityInfo;
import valorless.rarespawns.api.EntityUtils;
import valorless.rarespawns.datamodels.EntityData;
/**
* Ability: Hardened Iron
* Cancels projectile damage to the rare entity when its health drops below 50%.
* <p>
* This ability is used to make the rare entity immune to projectiles when low on health.
*/
@AbilityInfo(
name = "Hardened Iron",
cooldown = "10",
single = "true"
)
public class hardenediron implements Ability, Listener {
/**
* The rare entity this ability is attached to.
*/
Mob rare;
/**
* Executes the Hardened Iron ability, registering the event listener for projectile damage.
*
* @param data The EntityData for the rare entity.
* @param rare The Mob instance representing the rare entity.
*/
@Override
public void execute(EntityData data, Mob rare) {
this.rare = rare;
Bukkit.getServer().getPluginManager().registerEvents(this, Main.plugin);
}
/**
* Event handler for projectile damage to the rare entity.
* Cancels projectile damage if the rare entity's health is 50% or lower.
*
* @param event The EntityDamageByEntityEvent triggered by projectile damage.
*/
@EventHandler
public void onProjectile(EntityDamageByEntityEvent event) {
// Only trigger if the damaged entity is the rare entity
if(event.getEntity().getUniqueId() != rare.getUniqueId()) return;
// If health is 50% or lower, cancel projectile damage
if(EntityUtils.getHealthPercentage(rare) <= 50){
if(event.getCause() == DamageCause.PROJECTILE) event.setCancelled(true);
}
}
}- "No compiler available" or "ToolProvider.getSystemJavaCompiler() is null":
- Ensure you run the server with a JDK (not a JRE).
- "Class not found" or "Loaded class does not extend Ability":
- Check the class name and that it implements Ability.
- Ensure there is no package declaration.
- Ability fails silently at runtime:
- Check server logs for exceptions thrown during ability execution.
- Ensure your code catches exceptions or logs useful info (don't let unchecked exceptions kill the ability flow).