English
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: trueFacade
emaki.jiuwu.craft.mobs.api.EmakiMobsApi is a static facade:
| Method | Description |
|---|---|
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
| Method | Thread | Returns | Description |
|---|---|---|---|
definition(String mobId) | any thread | Optional<MobDefinition> | Look up a mob definition by id |
registeredIds() | any thread | Set<String> | List every registered mob id (immutable snapshot) |
identify(LivingEntity entity) | entity owner thread | Optional<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
| Method | Returns | Description |
|---|---|---|
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) | boolean | Refresh 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) | void | Remove 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
| Method | Description |
|---|---|
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:
| Field | Type | Description |
|---|---|---|
id() | String | The unique mob identifier used in YAML files and commands |
entityType() | EntityType | The underlying Minecraft entity type |
displayName() | @Nullable String | MiniMessage-formatted custom name; null when unset |
experience() | int | Experience 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::
| Constant | Key | Trigger that writes this context object |
|---|---|---|
ATTACKER | emakimobs:attacker | on_damage_take (when the damager is a living entity) |
KILLER | emakimobs:killer | on_death (when a living killer exists) |
VICTIM | emakimobs:victim | on_kill |
TARGET | emakimobs:target | on_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()
| Event | Cancellable | Key methods |
|---|---|---|
EmakiMobSpawnEvent | Yes | getEntity(), getMobId(), isCancelled() / setCancelled(boolean) |
EmakiMobDeathEvent | No | getEntity(), getMobId(), getKiller() |
EmakiMobSkillTriggerEvent | Yes | getMob(), 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 entitygetMobId(): 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,nullwhen 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 validationMobDefinitionexposes 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: emakimobsidentity with EmakiCoreLib's target selector (the identity value is the managed mob id) and unregisters it when the module disables. That lets CoreLib'sselectsource filter for EmakiMobs mobs; without EmakiMobs installed or enabled the same configuration raises no error, it simply matches no target