01Installation
SkBetterModels is a Skript addon, not a standalone plugin — it registers its syntax with Skript on enable and does nothing without it.
1
Install the two dependencies first
Drop Skript and BetterModel into /plugins. Both must already be present — plugin.yml declares depend: [Skript, BetterModel], so the server won't enable SkBetterModels without them.
2
Add SkBetterModels.jar
Place SkBettermodels-0_1_0.jar in /plugins alongside them, then restart (or /reload, though a full restart is safer for addon jars).
3
Confirm it loaded
Check the console log on startup for:
[SkBetterModels] SkBetterModels enabled — BetterModel syntax is now available in Skript.
If it's missing, the addon failed to register its syntax elements — check for a preceding SEVERE stack trace naming a missing class from BetterModel.
4
Write scripts as normal
Every syntax element on this page becomes available in any .sk file under plugins/Skript/scripts/ — no extra import or script header needed.
02Core concepts
A handful of ideas recur across almost every effect and condition below. Understanding these up front makes the reference section much faster to read.
Attach, then act
Nearly everything else assumes the entity already has a model tracker attached.
Flags you control
"Locked" and "paired" are metadata hooks for your own scripts, not built-in behaviour.
"model" in a sentence usually means "an entity with a BetterModel tracker attached." Most syntax takes %entities% and internally resolves each one's tracker — the BetterModel object that actually holds the mesh, bones, and animation state. If an entity has no model attached, these silently do nothing to it rather than erroring.
Attach a model before doing anything else to it. Almost every other effect/condition depends on the entity already having a tracker. The very first thing a script touching models should do is:
attach model "your_model_name" to {_entity}
The model name is whatever you named the model in BetterModel's own model files — this addon doesn't validate it beyond checking BetterModel actually has a renderer registered under that name.
Two different "animation" systems are exposed, and they overlap in practice.
set default state of model … to "…", play limb animation … on model …, and play bettermodel animation … on … all ultimately call the same underlying tracker.animate(name) method. They exist as separate, differently-worded effects for readability in scripts, not because they do different things internally.
play/stop player animation … on %players% calls that same method but is scoped to a player's own tracker (relevant if you attach cosmetic models directly to players, e.g. a limb/cape overlay) rather than to a separate mob entity.
Threading matters for the async sections. run async: and async loop … to …: execute their body off the main server thread. Bukkit and BetterModel APIs are not thread-safe — touching entities, the world, or model trackers from inside either of those without wrapping that part in run task sync: will cause unpredictable crashes or corruption. See Sections below for the correct pattern.
Locked / paired states are addon-managed, not read from BetterModel. model … is locked and model … is paired check for Bukkit metadata keys (skbettermodels_locked / skbettermodels_paired) on the entity. Nothing in this addon currently sets those keys — they're conditions waiting for you to set the metadata yourself (e.g. via a vanilla Skript effect or another plugin) as a lightweight flag system for your own scripts. See the known limitations section.
03Effects
Effects are actions you run on a line by themselves — attaching models, playing animations, changing appearance, and managing riders.
Attaching & identifying models
attach model
Creates a BetterModel tracker for a model and attaches it to one or more entities.
EFFECT
attach model %string% to %entities%
example
attach model "raptor_juvenile" to {_dino}
Looks up the model name against BetterModel's registered renderers. If no renderer exists under that name, this does nothing — no error, no broadcast.
Remembers the name. After attaching, the entity's model name is cached internally so event-model and other name lookups work without re-querying BetterModel.
Animation
set default state
Plays a named animation state on a model — typically used for idle/default loops.
EFFECT
set default state of model %entities% to %string%
example
set default state of model {_dino} to "idle"
Empty strings are ignored. Internally this is the same call as play limb animation — it exists as its own effect purely for naming clarity ("this is the animation the model rests in").
play bettermodel animation
Plays a named animation on a single entity's model and fires a corresponding Skript event.
EFFECT
play bettermodel animation %string% on %entity%
example
play bettermodel animation "attack" on {_dino}
Only effect that fires an event. This is the only animation-playing effect that also raises
on bettermodel animation play, giving you
event-model and
event-animation in a listener elsewhere. Use this one specifically when other scripts need to react to the animation starting.
play limb animation
Plays a named animation across one or more entities' models.
EFFECT
play limb animation %string% on model %entities%
example
play limb animation "roar" on model {_pack::*}
Accepts multiple entities in one call — good for pack-wide cues (e.g. every raptor in a pack roaring together).
play / stop player animation
Plays or stops a named animation on a player's own model tracker (not a separate mob entity).
EFFECT
play player animation %string% on %players%
stop player animation %string% on %players%
example
play player animation "wave" on player
stop player animation "wave" on player
Only affects players who already have a BetterModel tracker of their own — e.g. via a cosmetic/cape model attached directly to the player entity. Most players won't have one unless your setup specifically attaches models to players; on a player with no tracker, this silently no-ops.
stop all player animations
Stops every animation currently running on a player's own model tracker.
EFFECT
stop all player animations on %players%
example
stop all player animations on player
Experimental — implemented as a scan, not a native call. There's no single "stop everything" call in BetterModel's API — this works around that by checking every bone for a running animation and stopping each distinct name found. Functionally complete, but worth watching for edge cases.
Parts, appearance & visuals
set part visible / hidden
Shows or hides a named bone/part of a model.
EFFECT
set part %string% of model %entities% to (visible|hidden)
example
set part "tail" of model {_dino} to hidden
set part "head" of model {_dino} to visible
Part names match child bones by name only (not nested children of that bone). The current visibility is tracked internally, which is what powers the
model part … is visible condition.
change part
Replaces a named part of one model with a part taken from another model.
EFFECT
change part %string% of model %entities% to %string%
example
change part "tail" of model {_dino} to "spiked_tail"
Useful for cosmetic variation on the same base model — e.g. swapping a frill, tail, or crest part without attaching a whole separate model.
set tint
Applies a colour tint across a model.
EFFECT
set tint of model %entities% to %color%
example
set tint of model {_dino} to rgb(255, 80, 80)
Accepts any Skript colour value, including named colours and rgb(…). Useful for species colour variants, damage flashes, or team-colouring a tamed dinosaur.
set glow
Toggles the glowing outline effect on a model.
EFFECT
set glow of model %entities% to %boolean%
example
set glow of model {_dino} to true
apply / remove enchant glint
Toggles the shimmering enchantment overlay on a model.
EFFECT
apply enchant glint to model %entities%
remove enchant glint from model %entities%
example
apply enchant glint to model {_dino}
remove enchant glint from model {_dino}
Same on/off shape as set glow, but phrased as two separate commands rather than a boolean — a shimmer overlay rather than an outline.
set brightness
Sets the light level a model renders at, independent of the world around it.
EFFECT
set brightness of model %entities% to %number%
example
set brightness of model {_dino} to 15
Value is clamped to 0–15 (the same range as vanilla block/sky light levels). Applies the same value to both block-light and sky-light channels; there's currently no way to set them independently through this effect.
set body rotation
Sets how far a model's body can rotate independently of its head/facing direction.
EFFECT
set body rotation of model %entities% to %number%
example
set body rotation of model {_dino} to 90
The number sets a symmetric max/min — e.g. 90 allows the body to swing 90° in either direction from center. Larger values make a model's torso track its head more loosely; smaller values keep it rigid.
Hitboxes & riding
bind hitbox
Creates an interactable, mountable hitbox on a named bone of a model.
EFFECT
bind hitbox %string% to model %entities%
example
bind hitbox "saddle" to model {_dino}
Do this once per bone you want interactive — e.g. a "saddle" bone for mounting, or a "head" bone you want players to be able to click for a feeding interaction.
mount entity to model
Mounts a rider onto the first available rideable hitbox of a model.
EFFECT
mount %entity% to model %entity%
example
mount player to model {_dino}
Experimental — always mounts onto the first hitbox in the model's registry. Correct for a single-seat mount, but not seat-specific for a model exposing multiple named hitboxes (e.g. a two-rider mount). There's no way to target a specific named hitbox through this effect currently; see
known limitations.
Requires the target model to already have at least one hitbox — call
bind hitbox first if it doesn't.
dismount from model
Removes a specific rider, or every rider, from all of a model's hitboxes.
EFFECT
dismount %entity% from model %entity%
dismount all from model %entity%
example
dismount player from model {_dino}
dismount all from model {_dino}
The "all" form iterates every hitbox on the model and clears every rider from each — useful for forced dismounts (death, taming failure, a "buck off" mechanic).
Integrations
set model of citizen
Applies a BetterModel model to a Citizens NPC, by NPC ID.
EFFECT
set model of citizen %number% to %string%
example
set model of citizen 4 to "steve_dino"
Experimental — requires the Citizens plugin to also be installed. This is not declared as a hard dependency in plugin.yml, so the addon will still load without Citizens present, but this specific effect will fail to find the NPC (or throw, if Citizens' API classes aren't on the classpath at all) if it's missing.
Implemented by dispatching console commands (npc select then npc model) rather than calling a Citizens Java API directly — functionally equivalent to typing those commands yourself, so it inherits whatever behaviour Citizens' own model command has (including any of its own error messages appearing in console, not in Skript).
04Conditions
Boolean checks for use in if/while statements and elsewhere a condition is expected.
model is / isn't
Checks whether an entity's attached model matches a given name.
CONDITION
model %entities% (is|are) %string%
model %entities% (isn't|aren't) %string%
example
if model {_entity} is "blue_wizard":
broadcast "This is a blue wizard!"
Comparison is case-insensitive. With multiple entities, this behaves like a normal Skript group condition — it checks that all of them match (or, negated, that all of them don't) depending on how it's used in the surrounding sentence.
model part is visible
Checks whether a named part of a model is currently shown.
CONDITION
model part %string% of %entity% is visible
example
if model part "tail" of {_dino} is visible:
Reads from the same internal visibility map that
set part … visible/hidden writes to. A part that was never explicitly hidden is considered visible by default.
model has passenger
Checks whether a model currently has any rider mounted on it.
CONDITION
model %entities% (has|have) passenger
example
if model {_dino} has passenger:
This is a property condition, so it also supports Skript's standard negation wording (e.g. doesn't have passenger) automatically.
model is locked
Checks a metadata-backed "locked" flag on the entity.
CONDITION
model %entities% is locked
example
if model {_dino} is locked:
Experimental — no effect in this addon sets this flag. It checks for Bukkit metadata key
skbettermodels_locked on the entity — you're expected to set/remove that metadata yourself (e.g. via vanilla Skript's metadata effects, or another plugin) as a simple locking mechanism your scripts can build on. See
known limitations.
model is paired
Checks a metadata-backed "paired" flag on the entity.
CONDITION
model %entities% is paired
example
if model {_dino} is paired:
Experimental — same shape as model is locked. Checks Bukkit metadata key skbettermodels_paired, which nothing in this addon sets automatically. Intended as a flag for your own mating/pairing logic (e.g. for the dinosaur breeding system) to read and write.
has active player animations
Checks whether a player's own model tracker currently has any animation running.
CONDITION
%players% has active player animations
example
if player has active player animations:
Scans every bone on the player's tracker for a running animation. Requires the player to actually have a tracker attached (see the note on
play/stop player animation) — otherwise this is always false.
05Events
Triggers you can listen to with Skript's on …: syntax, plus one repeating-timer event.
on bettermodel spawn
Fires the moment a new model tracker is created for an entity.
EVENT
on bettermodel spawn:
example
on bettermodel spawn:
broadcast "a model appeared"
| Event value | Type | Notes |
| event-entity | Entity | The entity the new tracker belongs to. |
Fires for
any model spawn recognized by BetterModel — including ones created outside this addon (e.g. by BetterModel's own commands) — not only ones created via
attach model.
on bettermodel hitbox interact
Fires when a player interacts with (right-clicks) a hitbox bound to a model.
EVENT
on bettermodel hitbox interact:
example
on bettermodel hitbox interact:
broadcast "a hitbox was clicked"
| Event value | Type | Notes |
| event-player | Player | The player who clicked. |
| event-entity | Entity | The model entity the hitbox belongs to. |
Requires the hitbox to have been created first — via
bind hitbox or by BetterModel itself. A model with no hitbox never fires this.
on bettermodel hitbox enter
Fires when a player's position first overlaps a model's hitbox region.
EVENT
on bettermodel hitbox enter:
example
on bettermodel hitbox enter:
broadcast "%event-player% entered a BetterModel hitbox"
| Event value | Type | Notes |
| event-player | Player | The player who entered the region. |
| event-entity | Entity | The source entity the hitbox belongs to. |
Checked on a 1-tick repeating scan across every active model hitbox and every online player, on the same world — this is proximity-based ("standing inside the hitbox's rough bounding box"), not a click. Only fires once per continuous overlap (re-entering after leaving fires again).
Experimental — performance scales with hitbox count. Because this scans all hitboxes × all players every tick, a server with very many simultaneous models and hitboxes will spend proportionally more time on this check. Consider this if you're deploying dozens of dinosaurs with bound hitboxes to a busy server.
on bettermodel animation play
Fires whenever the play bettermodel animation effect runs.
EVENT
on bettermodel animation play:
example
on bettermodel animation play:
broadcast "%event-model%: %event-animation%"
| Event value | Type | Notes |
| event-entity | Entity | The entity the animation was played on. |
| event-model | Text | The model's name (see event-model). |
| event-animation | Text | The animation name that was played (see event-animation). |
Only fires from
play bettermodel animation specifically —
not from
play limb animation,
set default state, or
play player animation, even though all four ultimately trigger the same underlying animation call. If you need a listener to catch animations started via those other effects too, trigger this event's underlying pattern from your own script, or use
on bettermodel limb animation play for the limb-animation case specifically.
on bettermodel limb animation play
Fires whenever play limb animation runs on a model.
EVENT
on bettermodel limb animation play:
example
on bettermodel limb animation play:
broadcast "Limb animation: %event-animation%"
| Event value | Type | Notes |
| event-entity | Entity | The entity the animation was played on. |
| event-animation | Text | The animation name that was played. |
on bettermodel animation signal
Wraps BetterModel's own internal animation-signal event.
EVENT
on bettermodel animation signal:
example
on bettermodel animation signal:
# react to a raw signal from BetterModel's animation state machine
Experimental. No dedicated event-values are registered for this one — you're catching BetterModel's raw AnimationSignalEvent as a bare trigger. Useful mainly for advanced scripts that already know what they're listening for at the BetterModel level.
every N async ticks / seconds
A repeating timer that runs its body on an async scheduler thread instead of the main thread.
EVENT
every [%-number%] async tick[s]
every %number% async second[s]
example
every 5 async ticks:
# heavy computation off the main thread
every 2 async seconds:
# same idea, expressed in seconds
The number defaults to 1 if omitted in the ticks form. Seconds are converted to ticks internally (× 20) — both forms schedule the same kind of repeating async task.
This body runs off the main thread. Don't touch entities, the world, or BetterModel trackers directly inside it — wrap that part in
run task sync first. See
Core concepts.
06Expressions
Values you can read (and in one case, that are read-only outside their event) from anywhere an expression is expected.
model of %entity%
Returns the name of the model attached to an entity.
EXPRESSION
model of %entity%
%entity%'s model
example
broadcast "model: %model of {_dino}%"
broadcast "model: %{_dino}'s model%"
Returns nothing (an empty result) if the entity has no tracker attached. Both pattern forms are equivalent — use whichever reads more naturally in context.
event-model
The model name involved in the currently-firing event.
EXPRESSION
event-model
example
on bettermodel animation play:
broadcast "model was: %event-model%"
event-animation
The animation name involved in the currently-firing event.
EXPRESSION
event-animation
example
on bettermodel animation play:
broadcast "animation: %event-animation%"
Only meaningful inside
on bettermodel animation play. Note that
on bettermodel limb animation play exposes its animation name through a separately-registered event value of the same name, not through this expression class — both read as
event-animation in a script, but come from different underlying event types depending which block you're in.
07Sections
Multi-line blocks — each of these wraps an indented body of your own script and changes how or where that body runs.
run async
Runs its body on a background thread instead of the main server thread.
SECTION
run async:
example
run async:
broadcast "This is running asynchronously"
Prints its own diagnostic lines to console when it starts (which thread it hopped to) and if the body throws an error, including a reminder to use
run task sync for anything touching Bukkit/Minecraft state.
The outer script does not wait for this to finish — it continues immediately while the body runs in the background. Don't rely on statements after run async: seeing side effects from inside it unless you've synchronized some other way.
run task sync
Runs its body on the main server thread, and blocks the calling thread until it's done.
SECTION
run task (sync|synchronously):
example
async loop 1 to 1000:
run task sync:
broadcast "main-thread work here"
This is the required pairing for anything async that needs to touch the game world. Wrap the specific lines that read/write entities, blocks, or BetterModel trackers in this section when they live inside
run async or
async loop.
If the script is already on the main thread when it reaches this section (e.g. used outside any async context), it just runs the body immediately without switching threads — safe to use defensively even when you're not sure what thread you're on.
async loop
Runs its body once per whole number in a range, off the main thread.
SECTION
async loop %number% to %number%:
example
async loop 1 to 1000:
# heavy, non-Bukkit-API computation here
run task sync:
# anything touching entities/world/BetterModel goes here instead
Counts down instead of up automatically if the first number is larger than the second (e.g. async loop 10 to 1 counts 10, 9, 8 … 1).
The outer script continues immediately after this section is reached — the loop itself runs on a background task, so statements written after the loop in your script do not wait for all iterations to finish.
Experimental. The current iteration number is exposed inside the body as a raw event-number value from Skript's own vocabulary, not a dedicated named expression from this addon — reference it the way you'd reference any other event-number in a Skript event body.
08Known limitations
Alpha software has rough edges. Here's what to know before you build around a specific behaviour — and where to send a bug report if you find one that isn't listed.
Locked / paired are flags, not features. model … is locked and model … is paired read Bukkit metadata keys that nothing in v0.1.0 writes automatically. They're a hook for your own scripts (or another plugin) to set — for example, marking two dinosaurs as "paired" during your own mating logic, then reading that flag back elsewhere.
Mounting always targets the first hitbox. mount %entity% to model %entity% has no way to specify which named hitbox to mount onto when a model has more than one — it always picks whichever hitbox comes first in the model's internal registry. Fine for single-seat mounts; not seat-specific for multi-rider models.
"Stop all player animations" is a scan, not a native call. There's no single "stop everything" method in BetterModel's animation API — this effect works by checking every bone for a currently-running animation and stopping each one found. Functionally complete, but technically a workaround rather than one direct call.
Citizens integration is a soft dependency. set model of citizen requires the separate Citizens plugin, but Citizens is not declared in plugin.yml's depend list — the addon will enable fine without it, and only this one effect will fail (silently, or with a Citizens-side error in console) if Citizens isn't actually installed.
Hitbox-enter detection is a per-tick scan. on bettermodel hitbox enter checks every player against every hitbox on every active model tracker once per tick — proportionally more expensive the more models with bound hitboxes you have active at once. Keep this in mind at scale (e.g. many dozens of dinosaurs on a busy server).
event-animation means different things in different events. Inside on bettermodel animation play, event-animation comes from this addon's own expression class tied specifically to that event. Inside on bettermodel limb animation play, the same-looking expression is backed by a separately-registered event value on a different underlying event type. They read identically in script but are wired up independently — each only works inside its own matching event block.
This is alpha software. Anything on this page can change shape in a future version, and features marked experimental above are more likely to shift or get replaced outright as BetterModel's own API evolves. Pin your server to a known-working jar version if you're relying on this in production.