Developer API
EnchantsForge provides a clean developer API for creating and registering your own custom enchantments. You can extend the base CustomEnchant class, implement any of 5 event hooks, and register your enchant via EnchantRegistry.
Renamed: EnchantsForge was previously released as "CustomEnchants". The Java package (
com.cristian.customenchants), class names (CustomEnchant,EnchantRegistry,EnchantContext), and theplugin.ymldepend token are all preserved — only the plugin's display name and branding changed.
Adding the dependency
Add EnchantsForge as a dependency in your plugin.yml:
# plugin.yml
name: MyPlugin
version: 1.0
depend:
- EnchantsForge
Note: The
depend:token must match thename:field in the EnchantsForgeplugin.yml, which is nowEnchantsForge. If your plugin was previously depending onCustomEnchants, update this token.
Add the JAR to your build path (Maven/Gradle local file dependency or use a local Maven repository).
Creating a custom enchantment
Extend the CustomEnchant abstract class and implement the required methods:
import com.cristian.customenchants.api.CustomEnchant;
import com.cristian.customenchants.api.EnchantContext;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.Set;
public class MyEnchant extends CustomEnchant {
public MyEnchant(JavaPlugin plugin) {
super(plugin);
}
// ─── Required Methods ────────────────────────────────────────
@Override
public NamespacedKey getKey() {
return new NamespacedKey(plugin, "my_enchant");
}
@Override
public String getId() {
return "my_enchant"; // Used in commands: /enchant give my_enchant 1
}
@Override
public String getDisplayName() {
return "My Enchant"; // Shown in GUI and item lore
}
@Override
public int getMaxLevel() {
return 3;
}
@Override
public Set<Material> getApplicableTo() {
return Set.of(Material.DIAMOND_SWORD, Material.NETHERITE_SWORD);
}
// ─── Optional: Declare XP cost per level ─────────────────────
@Override
public int getXpCost(int level) {
return level * 10; // 10xp for I, 20xp for II, 30xp for III
}
// ─── Optional: Declare incompatible enchantments ─────────────
@Override
public Set<String> getConflicts() {
return Set.of("some_other_enchant");
}
}
Event hooks
Implement any combination of the 5 available hooks. You only need to override the hooks you want to use:
1. onMeleeHit — Melee Attack
@Override
public void onMeleeHit(EnchantContext.MeleeHit ctx) {
// ctx.getAttacker() — the player who attacked
// ctx.getVictim() — the entity that was hit
// ctx.getLevel() — the enchant level on the weapon
// ctx.getDamage() — the base damage of the hit
// ctx.setDamage(d) — modify the damage
int level = ctx.getLevel();
double heal = level * 2.0;
// Heal the attacker
double maxHp = ctx.getAttacker().getMaxHealth();
double newHp = Math.min(ctx.getAttacker().getHealth() + heal, maxHp);
ctx.getAttacker().setHealth(newHp);
}
2. onKill — Entity Kill
@Override
public void onKill(EnchantContext.Kill ctx) {
// ctx.getKiller() — the player who killed
// ctx.getVictim() — the entity that died
// ctx.getLevel() — enchant level
ctx.getKiller().sendMessage("You got a kill bonus from " + getDisplayName() + "!");
}
3. onTick — Passive Tick (runs periodically)
@Override
public void onTick(EnchantContext.Tick ctx) {
// ctx.getPlayer() — the player wearing/holding the enchanted item
// ctx.getLevel() — enchant level
// Called on a BukkitRunnable loop while the player is online
// Example: slowly regenerate hunger
if (ctx.getPlayer().getFoodLevel() < 20) {
ctx.getPlayer().setFoodLevel(ctx.getPlayer().getFoodLevel() + 1);
}
}
Note: The tick rate is managed by the base plugin. Do not create your own runnable for tick-based logic — use this hook instead.
4. onDamageTaken — Player Takes Damage
@Override
public void onDamageTaken(EnchantContext.DamageTaken ctx) {
// ctx.getPlayer() — the player wearing the enchanted armor
// ctx.getLevel() — enchant level
// ctx.getDamage() — incoming damage amount
// ctx.setDamage(d) — reduce or increase the damage
// Example: reduce damage by 10% per level
double reduction = ctx.getLevel() * 0.10;
ctx.setDamage(ctx.getDamage() * (1.0 - reduction));
}
5. onProjectileHit — Projectile (Arrow) Hit
@Override
public void onProjectileHit(EnchantContext.ProjectileHit ctx) {
// ctx.getShooter() — the player who fired the projectile
// ctx.getVictim() — the entity hit (may be null if hit a block)
// ctx.getLevel() — enchant level
// ctx.getProjectile()— the projectile entity
// Example: spawn a lightning bolt on arrow hit
if (ctx.getVictim() != null) {
ctx.getVictim().getWorld().strikeLightning(ctx.getVictim().getLocation());
}
}
Registering your enchantment
Register your enchantment in your plugin's onEnable() via the EnchantRegistry service:
import com.cristian.customenchants.api.EnchantRegistry;
import org.bukkit.Bukkit;
import org.bukkit.plugin.RegisteredServiceProvider;
public class MyPlugin extends JavaPlugin {
@Override
public void onEnable() {
// Get the EnchantRegistry from the ServicesManager
RegisteredServiceProvider<EnchantRegistry> provider =
Bukkit.getServicesManager().getRegistration(EnchantRegistry.class);
if (provider != null) {
EnchantRegistry registry = provider.getProvider();
registry.register(new MyEnchant(this));
getLogger().info("Registered MyEnchant!");
} else {
getLogger().severe("EnchantsForge API not found! Disabling.");
setEnabled(false);
}
}
}
Warning: Always check that the provider is not null — it will be null if EnchantsForge is not loaded. Make sure
EnchantsForgeis listed in yourplugin.ymldependarray.
Complete example
Here is a full minimal enchantment that poisons enemies on melee hit:
public class PoisonBlade extends CustomEnchant {
public PoisonBlade(JavaPlugin plugin) { super(plugin); }
@Override public NamespacedKey getKey() {
return new NamespacedKey(plugin, "poison_blade");
}
@Override public String getId() { return "poison_blade"; }
@Override public String getDisplayName() { return "Poison Blade"; }
@Override public int getMaxLevel() { return 3; }
@Override public Set<Material> getApplicableTo() {
return Set.of(Material.IRON_SWORD, Material.DIAMOND_SWORD, Material.NETHERITE_SWORD);
}
@Override
public void onMeleeHit(EnchantContext.MeleeHit ctx) {
int duration = ctx.getLevel() * 40; // 2s / 4s / 6s (in ticks)
ctx.getVictim().addPotionEffect(
new PotionEffect(PotionEffectType.POISON, duration, ctx.getLevel() - 1)
);
}
}
API summary
| Interface/Class | Description |
|---|---|
CustomEnchant |
Base class to extend for all custom enchantments |
EnchantRegistry |
Service for registering enchantments; obtained via ServicesManager |
EnchantContext.MeleeHit |
Context object for onMeleeHit hook |
EnchantContext.Kill |
Context object for onKill hook |
EnchantContext.Tick |
Context object for onTick hook |
EnchantContext.DamageTaken |
Context object for onDamageTaken hook |
EnchantContext.ProjectileHit |
Context object for onProjectileHit hook |