A P I

MythicCrucible ships a stable integration API (MythicCrucibleAPI), a set of Bukkit events, and, because every Crucible item is also a MythicMobs item, access to the MythicMobs API for item and skill work. Everything below lives in the MythicCrucible-API artifact, which is the only Crucible artifact you should depend on.

Repositories

The API is published to the same Lumine repository as MythicMobs. Its Paper API dependency comes from the PaperMC repository.

Maven

<repository>
    <id>nexus</id>
    <name>Lumine Releases</name>
    <url>https://mvn.lumine.io/repository/maven-public/</url>
</repository>
<repository>
    <id>papermc</id>
    <url>https://repo.papermc.io/repository/maven-public/</url>
</repository>

Gradle (Groovy)

repositories {
    // ...
    mavenCentral()
    maven { url 'https://mvn.lumine.io/repository/maven-public/' }
    maven { url 'https://repo.papermc.io/repository/maven-public/' }
}

Gradle (Kotlin)

repositories {
    // ...
    mavenCentral()
    maven(url = "https://mvn.lumine.io/repository/maven-public/")
    maven(url = "https://repo.papermc.io/repository/maven-public/")
}

Dependencies

MythicCrucible-API is the only artifact you need, and the only one you should ever depend on. It ships a sources jar, so your IDE will show the API's documentation inline.

Maven

<dependency>
    <groupId>io.lumine</groupId>
    <artifactId>MythicCrucible-API</artifactId>
    <version>5.13.1-SNAPSHOT</version>
    <scope>provided</scope>
</dependency>

Gradle (Groovy)

dependencies {
    // ...
    compileOnly 'io.lumine:MythicCrucible-API:5.13.1-SNAPSHOT'
}

Gradle (Kotlin)

dependencies {
    // ...
    compileOnly("io.lumine:MythicCrucible-API:5.13.1-SNAPSHOT")
}

Do not depend on MythicCrucible-Dist or any other Crucible artifact. Those are the complete paid plugin, they are not supported integration targets, and no new versions of them are published. MythicCrucible-API is the supported surface.

The API's MythicMobs (Mythic-Dist 5.13.0) and Paper API (1.21.4) dependencies come through transitively as compile-only dependencies, so you do not declare them yourself unless you need newer versions. The server must run MythicCrucible 5.13.1 or newer; older versions do not have this API and fail at runtime with NoSuchMethodError or NoClassDefFoundError. Documentation for the Mythic types used alongside this API (MythicItem, MythicConfig, SkillCaster, and so on) lives in the Mythic JavaDocs.

plugin.yml

Add MythicCrucible (and MythicMobs) to your depend so your plugin loads after them:

depend: [MythicMobs, MythicCrucible]

Getting the API

import io.lumine.mythiccrucible.api.MythicCrucibleAPI;
import io.lumine.mythiccrucible.api.MythicCrucibleProvider;

MythicCrucibleAPI crucible = MythicCrucibleProvider.get();

MythicCrucibleProvider.get() throws IllegalStateException until Crucible has enabled. With MythicCrucible in your plugin.yml depend, call it from your own onEnable(). Do not wait for MythicCrucibleLoadedEvent instead: it fires once, during Crucible's own enable, so a plugin that depends on Crucible registers its listeners too late to receive it.

Everything the API hands you is an interface from io.lumine.mythiccrucible.api.*, never one of Crucible's implementation classes, so your code keeps compiling as Crucible's internals change. The furniture placement helpers are expressed in stable terms (String item ids, Bukkit Block/Player, primitives and Optionals) and degrade gracefully, returning empty or false when an id is unknown, a block holds no furniture, or the plugin is not ready. The richer lookups return API interfaces such as CrucibleItem, Furniture, FurnitureItemContext, CustomBlockItemContext and Profile.

These interfaces are implemented by Crucible only. Do not implement them yourself; passing your own implementation back into the API is rejected. The one exception is io.lumine.mythiccrucible.items.SkillHolder, which you implement to run your own skills on a player (see Running your own skills on a player).

Looking things up

import io.lumine.mythiccrucible.api.items.CrucibleItem;
import io.lumine.mythiccrucible.api.furniture.Furniture;

// items, by id or from a live stack
Optional<CrucibleItem> byId = crucible.getItem("MyChair");
Optional<CrucibleItem> byStack = crucible.getItem(itemStack);
Collection<String> everyItemId = crucible.getItemNames();

// custom blocks
crucible.getCustomBlock(block).ifPresent(cb -> {
    String id = cb.getCrucibleItem().getInternalName();
});

// furniture, from a block or from any of its entities (frame, hitbox or seat)
Optional<Furniture> f1 = crucible.getFurniture(block);
Optional<Furniture> f2 = crucible.getFurniture(entity);

A Furniture is a handle bound to one frame entity. Tracked furniture (furniture with timer or random-tick skills, or with Tracked: true, which defaults on when Variables or KeepVariablesOnDrop is set) always returns the same handle, and rotation re-points it at the respawned frame. Other furniture returns a new handle per lookup, and a handle obtained before a rotation keeps pointing at the removed frame. A skill that sets a variable on untracked furniture tracks it until it is rotated or its chunk unloads. No handle is invalidated when the piece is removed, so re-resolve it after a removal or rotation rather than caching it.

Examples

Check for furniture at a block

MythicCrucibleAPI crucible = MythicCrucibleProvider.get();
Block block = ...;

if (crucible.isFurniture(block)) {
    crucible.getFurnitureItemId(block).ifPresent(id -> {
        // id is the Crucible item id of the furniture at this block
    });
}

Place furniture

boolean placed = crucible.placeFurniture("MyChair", block, 90f);

For feedback on why placement failed, use the result variant:

import io.lumine.mythiccrucible.api.FurniturePlacementResult;

FurniturePlacementResult result = crucible.placeFurnitureResult("MyChair", block, 90f);
switch (result) {
    case SUCCESS -> player.sendMessage("Placed!");
    case OBSTRUCTED -> player.sendMessage("Not enough room.");
    case NO_SUPPORTING_BLOCK -> player.sendMessage("Needs a solid surface to rest on.");
    case UNSUPPORTED_PLACEMENT -> player.sendMessage("Can't place that here.");
    case UNKNOWN_ITEM, NOT_FURNITURE -> player.sendMessage("That isn't a furniture item.");
    default -> player.sendMessage("Could not place that.");
}

These methods place on the floor, or at the furniture's default placement when it cannot go on the floor (ceiling-only furniture goes on the ceiling). Wall-only furniture needs the face overload, which takes the face a player would click: BlockFace.UP for the floor, BlockFace.DOWN for a ceiling, or NORTH/SOUTH/EAST/WEST for a wall (NORTH hangs the furniture on the block south of block). It returns UNSUPPORTED_PLACEMENT when the furniture cannot attach to that face, instead of falling back to another one:

FurniturePlacementResult result = crucible.placeFurnitureResult("MyPainting", block, BlockFace.NORTH, 0f, player);

Placement does not return the placed piece. To get its handle, look it up at the same block afterwards:

if (crucible.placeFurnitureResult("MyChair", block, 90f).isSuccess()) {
    Furniture placed = crucible.getFurniture(block).orElseThrow();
}

Clear terrain before placing large furniture

getFurnitureFootprint returns exactly the blocks placeFurniture checks (the base block plus rotated Barriers/Lights offsets), so you can clear them first, then place:

List<Block> footprint = crucible.getFurnitureFootprint("BigStatue", block, 0f);
for (Block b : footprint) {
    b.setType(Material.AIR);
}
crucible.placeFurniture("BigStatue", block, 0f);

Remove furniture

// remove, running break skills + drop table, and dropping the stored inventory
crucible.removeFurniture(block, player, true, true);

FurnitureItemContext#remove throws IllegalArgumentException when the Furniture belongs to a different item than that context.

Generate a Crucible item as an ItemStack

Every Crucible item wraps a MythicMobs item, which you reach through the API rather than through Crucible's internals:

import io.lumine.mythic.bukkit.BukkitAdapter;

crucible.getItem("MyCrucibleItem").ifPresent(item -> {
    ItemStack stack = BukkitAdapter.adapt(item.getMythicItem().generateItemStack(1));
    player.getInventory().addItem(stack);
});

Running your own skills on a player

Implement io.lumine.mythiccrucible.items.SkillHolder and register it on the player's profile; Crucible then runs its timer skills and triggers alongside the player's own items:

import io.lumine.mythiccrucible.items.SkillHolder;

crucible.getProfile(player).ifPresent(profile ->
        profile.whenLoaded(() -> profile.registerExternalHolder(myHolder)));

// when it no longer applies
crucible.getProfile(player).ifPresent(profile -> profile.unregisterExternalHolder(myHolder));
  • getProfile is empty for offline players. Profiles live for one session and are dropped shortly after the player quits, so register again when the player joins.
  • A profile loads asynchronously after join. Registered holders do not run until it has loaded. whenLoaded runs its action immediately if the profile has loaded, otherwise later on the main thread.
  • runTimerSkills is called from Crucible's asynchronous clock, not the main thread.
  • While the player holds a Crucible item, the ~onUse trigger goes to that item and is not passed to your holder.
  • An exception or linkage error thrown by your holder is logged and skipped; it does not stop other holders or players.

Events

Bukkit events fired by MythicCrucible. A checkmark in the Cancellable column means the event implements Cancellable.

Furniture

Event Description Cancellable
MythicFurniturePlaceEvent Fired when a player places a furniture item. Exposes the player, block, block face, yaw and color (the item's configured color when it carries none). The block face is the direction of the supporting block (DOWN for the floor), the opposite of the clicked face. API placement does not fire it. yes
MythicFurnitureRemoveEvent Fired when a furniture is removed or broken; rotating a furniture does not fire it. Exposes the breaker and the furniture. When break skills run, an ~onBlockBreak skill can still cancel the removal after this event. yes
MythicFurnitureRotateEvent Fired when a player rotates a furniture. Exposes the player and the furniture. yes
MythicFurnitureTrackEvent Fired on the main thread when Crucible starts tracking a furniture: a tracked furniture (see Looking things up) is placed, rotated or its chunk loads, or a skill sets a variable on an untracked one.
MythicFurnitureUntrackEvent Fired when Crucible stops tracking a furniture: it is removed or rotated, its entity leaves the world, or Crucible reloads its items. Rotating a tracked furniture fires Untrack for the old frame, then Track for the same handle on the new frame. After a reload only furniture with timer or random-tick skills or Tracked: true is tracked again, with new handles.

Lifecycle

Event Description Cancellable
MythicCrucibleLoadedEvent Fired once, during MythicCrucible's enable. Plugins that depend on Crucible enable later and never receive it. Exposes the Bukkit Plugin instance.
MythicCrucibleReloadedEvent Fired after MythicCrucible has reloaded its items: after /mm reload, and when Nexo (re)loads its items, which happens once shortly after startup and on /nexo reload. CrucibleItem objects from before the reload are stale; look them up again. Exposes the Bukkit Plugin instance.
MythicCrucibleGeneratePackEvent Fired after MythicCrucible finishes generating a resource pack, once the zip (and PackSquash, if enabled) has been fully written. Exposes the generated zip file, or the pack output folder when ZipPack is disabled.

Crafting

Event Description Cancellable
MythicCraftItemEvent Fired for a craft in Crucible's custom crafting system, immediately before the result is applied. Exposes the recipe key, the craft count, the result item(s) and the triggering InventoryClickEvent. yes
MythicCraftItemCompleteEvent Fired after a craft in Crucible's custom crafting system has happened. getTimes() is the number of crafts performed. Exposes the same recipe key, result item(s) and triggering event.
@EventHandler
public void onCraft(MythicCraftItemEvent event) {
    NamespacedKey recipe = event.getRecipeKey();
    int times = event.getTimes();
    ItemStack result = event.getResult();
}

getTimes() is the planned count, clamped to 1 for a non-shift click; a shift-click with room for fewer crafts still reports the planned figure, because the achievable count is computed after this event. A craft that is not cancelled can still fail afterwards (the cursor cannot stack the result, or the inventory has no room), so count crafts with MythicCraftItemCompleteEvent, whose getTimes() is the achieved count. getResult() is the station's display stack, so treat it as identifying what is being crafted rather than as the stack the player receives: its amount is not meaningful, and a command recipe reports its display item even though the player receives nothing. Compare recipes with getRecipeKey(), using NamespacedKey#equals rather than parsing the key string.

Migrating from MythicCrucible-Dist

Plugins compiled against the old MythicCrucible-Dist jar must be rebuilt against MythicCrucible-API. The furniture event getters now return the API interfaces (io.lumine.mythiccrucible.api.furniture.Furniture and FurnitureItemContext), MythicCrucibleLoadedEvent#getPlugin returns a Bukkit Plugin, and MythicCraftItemEvent no longer has getResultInventory, getOtherInventories, getCache, getMapping or setMapping. Old builds throw NoSuchMethodError inside those listeners. In most cases the source change is only swapping imports to the io.lumine.mythiccrucible.api packages.

Custom Mechanics, Conditions, Placeholders & Triggers

Crucible's own item mechanics, conditions, targeters, placeholders and triggers are built with the exact same annotation-based system MythicMobs uses (@MythicMechanic, @MythicCondition, @MythicTargeter, @MythicPlaceholder, and SkillTrigger). There is nothing Crucible-specific about registering them, so to add your own follow the MythicMobs API guide (its "Registering Custom Components" section). Custom triggers are dispatched through the same Mythic event bus.

Updated Sep 23, 2026