org.nexomaker.api, documented in the rest of this section. Underneath it sits the embedded BetterModel engine, whose own API ships in the same jar under kr.toxicity.model.api — and that is where models, animations, bones and hitboxes live.
That package is not relocated when the plugin jar is shaded, so your addon can call it directly with no extra dependency. NMEntitiesAPI deliberately does not wrap it: the engine API is large, well-documented in its own javadoc, and re-exporting it would only add a layer to keep in step.
Bukkit types to platform types
The engine is platform-agnostic, so it speaksPlatformEntity, PlatformPlayer, PlatformLocation rather than Bukkit types. One static class converts:
Finding a model
BetterModel.limb(name), limbOrNull(name), limbs(), limbKeys().
A model name is the .bbmodel filename without its extension — the same string a mob’s model: field takes. Note that a model being loaded is not the same as it being a registered mob; the engine sees every model in models/, the /nme commands only see the ones declared as mobs.
Putting a model on any entity
This is the thing config can’t do: attach a model to an entity NMEntities did not spawn — another plugin’s boss, a vanilla mob, an armour stand you placed yourself.create(...) always builds a fresh tracker; getOrCreate(...) reuses one already on that entity. Both take an optional TrackerModifier and an optional Consumer that runs before the first update, so you can configure the tracker before any packet goes out.
To find what is already attached, go through the registry:
Animations
animate returns false when the model has no animation by that name. For anything beyond “play it”, pass an AnimationModifier:
predicate (a BooleanSupplier gating whether it plays), start and end keyframes, priority, loop type, speed (fixed or a supplier, so it can follow the mob’s movement speed), override, and player for an animation only one viewer sees. AnimationModifier.DEFAULT and DEFAULT_WITH_PLAY_ONCE are there for the common cases.
replace(target, animation, modifier) swaps one animation for another while it is running — the clean way to change gait without a visible reset.
The effect DSL’s animation mechanics drive this same tracker, so an addon playing an animation from code and a config line doing it produce identical results.
Who can see it
Bones
Every bone in the Blockbench model is aRenderedBone you can reach and restyle at runtime:
brightness, glow, glowColor, viewRange, tint, previousTint, enchant, togglePart, itemStack, billboard, itemMapping, moveDuration, plus composite(...) to apply several at once and perBone(...) to compute a different action per bone.
BonePredicate.name("head") matches by name, BonePredicate.tag(...) by bone tag, and BonePredicate.TRUE hits every bone. Predicates compose with and, or and negate.
Hitboxes and headshots
Models carry per-bone hitboxes, and a tracker can listen to what happens to them. This is how you build damage zones today:HitBoxDamagedEvent is cancellable and its damage is mutable, so you can multiply, floor or veto per bone. HitBoxInteractAtEvent gives you the same for right-clicks, carrying the player, the hand and the exact hit position on the bone.
Configurable damage zones — headshot multipliers declared in a mob’s YAML — are on the roadmap. Until then this listener is the supported way to do it, and it is what that feature will be built on.
Engine events
The engine has its own event type,ModelEvent, rather than one Bukkit event class per event. There are two ways to listen.
Through Bukkit. Every engine event is delivered wrapped in a single BetterModelBukkitEvent:
BukkitEventApplication holds a weak reference to your plugin and checks that it is still enabled, so a disabled addon stops receiving events without leaking.
The events
AnimationSignalEvent is worth calling out: you place a signal on a keyframe in Blockbench and it reaches your addon at exactly that frame — sound on footfall, damage on the frame the sword lands.
NMEntities’ own events — mob spawns, NPC interactions, reloads — are ordinary Bukkit events and live in org.nexomaker.api.event.
