Skip to main content

🧩 Mechanics (Developers)

Overview​

Mechanics are item-bound behaviors in Oraxen. A mechanic is configured per-item under mechanics: in your item YAML, and Oraxen applies the mechanic during item parsing.

info

Oraxen is open source, allowing you to create a pull request with the needed changes for your integration. If you are unsure about how to contribute, refer to our Contributing Guide.

warning

Minecraft 1.20.5+ introduces vanilla item components. Some legacy mechanics (like durability) are deprecated in favor of components. Use mechanics for custom behavior; use components: for vanilla-backed data when possible.

Usage​

info

A custom mechanic usually has three parts.

  • Mechanic - holds the per-item configuration and optional item modifiers
  • MechanicFactory - parses the item config, caches mechanics by item id, registers listeners/tasks, cleans up on unregister
  • One or more Listeners that implement the behavior

Core types:

// Per-item mechanic instance
public abstract class Mechanic {
protected Mechanic(MechanicFactory factory, ConfigurationSection section,
Function<ItemBuilder, ItemBuilder>... modifiers) { ... }
}

// Per-mechanic parser + registry
public abstract class MechanicFactory {
public abstract Mechanic parse(ConfigurationSection itemMechanicConfiguration);
public void onUnregister() { ... }
public List<MechanicConfigProperty> getConfigSchema() { ... }
protected void addToImplemented(Mechanic mechanic) { ... }
}

Create the Mechanic Class​

This example mechanic stores a multiplier on the item and uses it later during combat.

import io.th0rgal.oraxen.OraxenPlugin;
import io.th0rgal.oraxen.items.ItemBuilder;
import io.th0rgal.oraxen.mechanics.Mechanic;
import io.th0rgal.oraxen.mechanics.MechanicFactory;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.persistence.PersistentDataType;

public class DamageMultiplierMechanic extends Mechanic {
public static final NamespacedKey KEY = new NamespacedKey(OraxenPlugin.get(), "damage_multiplier");
private final double multiplier;

public DamageMultiplierMechanic(MechanicFactory factory, ConfigurationSection section) {
super(factory, section, (ItemBuilder item) ->
item.setCustomTag(KEY, PersistentDataType.DOUBLE, section.getDouble("multiplier", 1.0))
);
this.multiplier = section.getDouble("multiplier", 1.0);
}

public double getMultiplier() {
return multiplier;
}
}

Create the Factory Class​

The factory parses the per-item config, caches mechanics via addToImplemented, and registers your listeners using MechanicsManager so Oraxen can unload them on reload. Override onUnregister() if your mechanic owns additional resources.

import io.th0rgal.oraxen.mechanics.Mechanic;
import io.th0rgal.oraxen.mechanics.MechanicConfigProperty;
import io.th0rgal.oraxen.mechanics.MechanicFactory;
import io.th0rgal.oraxen.mechanics.MechanicsManager;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;

import java.util.List;

public class DamageMultiplierMechanicFactory extends MechanicFactory {
public DamageMultiplierMechanicFactory(JavaPlugin plugin, ConfigurationSection section) {
super(section);
MechanicsManager.registerListeners(plugin, getMechanicID(), new DamageMultiplierListener(this));
}

@Override
public Mechanic parse(ConfigurationSection itemMechanicConfiguration) {
Mechanic mechanic = new DamageMultiplierMechanic(this, itemMechanicConfiguration);
addToImplemented(mechanic);
return mechanic;
}

@Override
public @NotNull List<MechanicConfigProperty> getConfigSchema() {
return List.of(
MechanicConfigProperty.decimal("multiplier", "Damage multiplier applied on hit", 1.0, 0.0)
);
}
}

Implement the Behavior (Listener)​

import io.th0rgal.oraxen.api.OraxenItems;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.inventory.ItemStack;

public class DamageMultiplierListener implements Listener {
private final DamageMultiplierMechanicFactory factory;

public DamageMultiplierListener(DamageMultiplierMechanicFactory factory) {
this.factory = factory;
}

@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onDamage(EntityDamageByEntityEvent event) {
if (!(event.getDamager() instanceof Player player)) return;
ItemStack item = player.getInventory().getItemInMainHand();
if (!OraxenItems.exists(item)) return;

DamageMultiplierMechanic mechanic = (DamageMultiplierMechanic) factory.getMechanic(item);
if (mechanic == null) return;

event.setDamage(event.getDamage() * mechanic.getMultiplier());
}
}

Configure the Mechanic​

Enable it in plugins/Oraxen/mechanics.yml (global mechanic settings).

damage_multiplier:
enabled: true

Use it on any item under mechanics:.

my_sword:
material: DIAMOND_SWORD
mechanics:
damage_multiplier:
multiplier: 1.25

Register the Mechanic​

Register your factory on OraxenNativeMechanicsRegisteredEvent (this event is fired again on /oraxen reload all, and the registry is cleared during reload).

import io.th0rgal.oraxen.OraxenPlugin;
import io.th0rgal.oraxen.api.OraxenItems;
import io.th0rgal.oraxen.api.events.OraxenNativeMechanicsRegisteredEvent;
import io.th0rgal.oraxen.mechanics.MechanicsManager;
import org.bukkit.Bukkit;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.plugin.java.JavaPlugin;

public class MyOraxenMechanicsPlugin extends JavaPlugin implements Listener {
@Override
public void onEnable() {
Bukkit.getPluginManager().registerEvents(this, this);
}

@EventHandler
public void onMechanicsRegistered(OraxenNativeMechanicsRegisteredEvent event) {
ConfigurationSection section = OraxenPlugin.get().getResourceManager()
.getMechanics().getConfigurationSection("damage_multiplier");
if (section == null) return;

boolean enabled = section.getBoolean("enabled", true);
MechanicsManager.registerMechanicFactory(
"damage_multiplier",
new DamageMultiplierMechanicFactory(this, section),
enabled
);

// Re-parse items so the new mechanic is applied.
OraxenItems.loadItems();
}
}

Example​

If you prefer learning from existing implementations, browse the built-in mechanic factories in the Oraxen codebase.

  • src/main/java/io/th0rgal/oraxen/mechanics/provided/gameplay/durability/DurabilityMechanicFactory.java (deprecated on 1.20.5+)
  • src/main/java/io/th0rgal/oraxen/mechanics/provided/gameplay/togglelight/ToggleLightMechanicFactory.java
  • src/main/java/io/th0rgal/oraxen/mechanics/provided/misc/soulbound/SoulBoundMechanicFactory.java

Notes​

info
  • Mechanic instances are created per item during item parsing.
  • Use addToImplemented(mechanic) in your factory so you can later query it with factory.getMechanic(itemStack) or factory.getMechanic(itemId).
  • Prefer MechanicConfigProperty / @ConfigProperty to expose your mechanic's config schema for tools.
  • MechanicsManager.unregisterMechanicFactory(...) calls onUnregister(), unloads registered listeners, and cancels registered tasks.