Skip to content

API and Integration ​

Third-party plugins reach EmakiMobs' mob definitions through emaki-mobs-api (current version 1.0.9). The API jar is a compile-time dependency only; the matching runtime artifact is EmakiMobs-1.0.13.jar

Maven coordinates ​

xml
<repositories>
  <repository>
    <id>jiuwu-releases</id>
    <url>https://repo.crypticlib.com/repository/maven-public/</url>
  </repository>
</repositories>

<dependency>
  <groupId>emaki.jiuwu.craft</groupId>
  <artifactId>emaki-mobs-api</artifactId>
  <version>1.0.9</version>
  <scope>provided</scope>
</dependency>
kotlin
repositories { maven("https://repo.crypticlib.com/repository/maven-public/") }
dependencies { compileOnly("emaki.jiuwu.craft:emaki-mobs-api:1.0.9") }

Compile-time dependency only

The runtime jar already embeds its API classes without relocation. Never install, bundle, shade, or relocate the API jar: a duplicate ClassLoader type breaks events and bridges

Declare EmakiMobs as a dependency in your paper-plugin.yml:

yaml
dependencies:
  server:
    EmakiMobs:
      load: BEFORE
      required: false
      join-classpath: true

Facade ​

emaki.jiuwu.craft.mobs.api.EmakiMobsApi is a static facade:

MethodDescription
status()Availability and identity metadata; returns CoreLib's ApiStatus
catalog()Query layer, see MobCatalog below
operations()Operation layer, see MobOperations below
extensions()Extension layer, see MobExtensions below

Accessors never return null: when EmakiMobs is absent the catalog answers empty, so callers must not treat a NullPointerException as an availability signal

MobCatalog query layer ​

Annotated @ApiStatus.NonExtendable

MethodThreadReturnsDescription
definition(String mobId)any threadOptional<MobDefinition>Look up a mob definition by id
registeredIds()any threadSet<String>List every registered mob id (immutable snapshot)
identify(LivingEntity entity)entity owner threadOptional<String>Read the entity's PDC and return the managed mob id currently mounted on it; returns empty for a null, unmanaged, or not-yet-ready entity, or when its definition is no longer loaded

definition and registeredIds return immutable snapshots with an any-thread contract; identify reads live entity state and is contractually restricted to the entity owner thread

MobOperations operation layer ​

MethodReturnsDescription
spawn(Location location, String mobId)Optional<LivingEntity>Spawn a registered custom mob at the given location. location must have a non-null world and mobId is case-sensitive; returns an empty Optional when the id is unknown or spawning fails
refresh(LivingEntity entity)booleanRefresh one managed entity using the non-destructive refresh rules. This synchronous method reads and writes live entity state, must be called on the entity owner thread, and does not schedule work on the caller's behalf; it returns false for a null, invalid, dead, unmanaged, unknown, or unavailable entity
remove(LivingEntity entity)voidRemove a managed entity. The interface default is a no-op; the active runtime bridge supplies the real implementation

While EmakiMobs is absent or not yet ready, operations() returns a safe no-op implementation whose spawn always yields Optional.empty() and whose refresh always returns false

Spawning an entity mutates Bukkit state: on Folia, switch to the owner thread for that location before calling it; on Paper that owner thread is the main thread

MobExtensions extension layer ​

MethodDescription
registerCustomSpawner(Plugin owner, String id, CustomSpawner spawner)Register a custom spawner and return a closeable MobSpawnerRegistration handle; id is used for deduplication. owner is the plugin that owns the registration and may be null, but then automatic cleanup on that plugin's disable is unavailable
unregisterCustomSpawners(Plugin owner)Remove every custom spawner registered by that owner

MobExtensions.CustomSpawner is a functional interface nested inside MobExtensions, with a single onReload() callback: it is invoked once immediately on registration, then again after every EmakiMobs config reload. Use it to read updated config values and (re) schedule your own spawn tasks

java
MobSpawnerRegistration handle = EmakiMobsApi.extensions()
        .registerCustomSpawner(this, "my_spawner", () -> {
            // Runs on registration and after every reload
            reloadMySpawnConfig();
        });

// Close the handle when the registration is no longer needed
handle.close();

MobDefinition model ​

Exposed fields:

FieldTypeDescription
id()StringThe unique mob identifier used in YAML files and commands
entityType()EntityTypeThe underlying Minecraft entity type
displayName()@Nullable StringMiniMessage-formatted custom name; null when unset
experience()intExperience override on kill; 0 means use the vanilla default

Internal implementation that is not exposed: components, ea_attributes, actions, threat, and boss_bar are not part of the public API. They are internal configuration and cannot be queried by third-party plugins

MobActionKeys action context keys ​

emaki.jiuwu.craft.mobs.api.MobActionKeys exposes four CoreActionKey<LivingEntity> constants namespaced under emakimobs::

ConstantKeyTrigger that writes this context object
ATTACKERemakimobs:attackeron_damage_take (when the damager is a living entity)
KILLERemakimobs:killeron_death (when a living killer exists)
VICTIMemakimobs:victimon_kill
TARGETemakimobs:targeton_target, on_damage_give (when the target is a living entity)

These keys only exist in the action context of the matching trigger; see Triggers for the trigger-to-context mapping

Do not cache layer objects ​

Resolve catalog() at the point of use instead of storing it in a field: the backing bridge is replaced on reload

java
// Correct
EmakiMobsApi.catalog().definition("elite_zombie");

// Wrong: this reference points at the old bridge after a reload
private final MobCatalog cached = EmakiMobsApi.catalog();

Example ​

java
import emaki.jiuwu.craft.mobs.api.EmakiMobsApi;
import emaki.jiuwu.craft.mobs.api.MobCatalog;
import emaki.jiuwu.craft.mobs.api.model.MobDefinition;
import org.bukkit.entity.EntityType;

public class MyPlugin extends JavaPlugin {
    @Override
    public void onEnable() {
        if (!EmakiMobsApi.status().usable()) {
            getLogger().warning("EmakiMobs API is unavailable");
            return;
        }

        MobCatalog catalog = EmakiMobsApi.catalog();

        // Look up a single mob definition
        catalog.definition("elite_zombie").ifPresent(mob -> {
            getLogger().info("Found mob: " + mob.displayName()
                + " (type: " + mob.entityType()
                + ", exp: " + mob.experience() + ")");
        });

        // List every registered mob
        getLogger().info("Registered " + catalog.registeredIds().size() + " mob types");
        for (String mobId : catalog.registeredIds()) {
            catalog.definition(mobId).ifPresent(mob -> {
                if (mob.entityType() == EntityType.ZOMBIE) {
                    getLogger().info("  - " + mobId + " (zombie variant)");
                }
            });
        }
    }
}

Events ​

The emaki.jiuwu.craft.mobs.api.event package exposes three managed-mob lifecycle events. All are standard Bukkit events, listened to with @EventHandler; each event class ships its own static getHandlerList()

EventCancellableKey methods
EmakiMobSpawnEventYesgetEntity(), getMobId(), isCancelled() / setCancelled(boolean)
EmakiMobDeathEventNogetEntity(), getMobId(), getKiller()
EmakiMobSkillTriggerEventYesgetMob(), getMobId(), getTrigger(), isCancelled() / setCancelled(boolean)

All three fire synchronously, on the owner thread of the entity involved: the main server thread on Paper, or the region thread on Folia. Do not run blocking work in these callbacks

EmakiMobSpawnEvent ​

Fired after EmakiMobs' MobFactory has created, marked, and synchronously configured a managed mob; at that point its boss bar, deferred health top-up, and on_spawn action pipeline are not yet registered or run

  • getEntity(): the newly created and configured managed entity
  • getMobId(): the loaded mob definition id
  • Cancelling it removes the newly created entity and makes the spawning operation return an empty Optional
  • It only covers entities created through the EmakiMobs mob factory; vanilla entities later marked by type-override rules do not fire it

EmakiMobDeathEvent ​

Fired after EmakiMobs has finished applying its experience and drop handling for a managed mob death

  • This event is observational and cannot be cancelled; it cannot change the death outcome
  • getEntity(): the dying managed entity; getMobId(): the loaded definition id; getKiller(): the Bukkit killer, null when no killer exists
  • It fires only when the dying entity still has a managed mob id whose definition remains loaded; a stale PDC id or no managed id does not fire it

EmakiMobSkillTriggerEvent ​

Fired after a managed mob trigger resolves at least one action pipeline and before any resolved pipeline runs

  • getMob(): the managed entity executing the trigger; getMobId(): the loaded definition id; getTrigger(): the id of the trigger about to run
  • Cancelling it suppresses every pipeline for this invocation
  • No event is emitted when CoreLib is unavailable, the mob definition is absent, or no executable pipeline resolves

Integration notes ​

  • Use definition() to check whether an id belongs to an EmakiMobs-managed custom mob when a mob spawns or is interacted with
  • registeredIds() is useful for tab completion and configuration validation
  • MobDefinition exposes a small field set intended for basic information display. When you need more detail (components, ea_attributes, and so on), read EmakiMobs' configuration files directly or use entity PDC markers
  • While enabled, EmakiMobs registers a type: emakimobs identity with EmakiCoreLib's target selector (the identity value is the managed mob id) and unregisters it when the module disables. That lets CoreLib's select source filter for EmakiMobs mobs; without EmakiMobs installed or enabled the same configuration raises no error, it simply matches no target