Custom Molang queries allow you to expose additional data to your animations. These queries can be registered and updated through your animator implementations.
Defining Custom Queries
Step 1: Define Query Constants
First, define your custom query constants in your mods main class:
public static final String MY_CUSTOM_VALUE = MolangQueries.normalize("query.my_custom_value");
Step 2: Register Variables
Create a method to register your variables, typically called during initialization. Register against the shared MolangParser.INSTANCE — don't create a new MolangParser, since doing so just re-runs its constructor setup for no benefit:
private void registerXModsVariables() {MolangParser.INSTANCE.register(new LazyVariable(ModClass.MY_CUSTOM_VALUE, 0));}
Amaran
Register your variables during mod initialization, before resources load. Animations bind to a variable when they are loaded, so a variable registered (or re-registered) afterwards isn't seen by animations that are already loaded until the next resource reload.
Step 3: Implement Query Updates
Override applyMolangQueries in your AzAnimator class to update your query values during animation. Create your suppliers once and have them read the entity currently being animated, rather than creating a new lambda every frame. Binding then allocates nothing:
public class MyEntityAnimator extends AzEntityAnimator<MyEntity> {// Resolved once, instead of looking the query up by name every frame.private static final MolangVariableRef MY_CUSTOM_VALUE_REF = new MolangVariableRef(ModClass.MY_CUSTOM_VALUE);// The entity currently being animated, set in applyMolangQueries below.private MyEntity currentEntity;// Created once, when the animator is created.private final DoubleSupplier myCustomValue = () -> currentEntity.getChargeLevel();@Overrideprotected void applyMolangQueries(MyEntity entity, double animTime, float partialTicks) {super.applyMolangQueries(entity, animTime, partialTicks);this.currentEntity = entity;MY_CUSTOM_VALUE_REF.setMemoized(myCustomValue);}// ... registerControllers, getAnimationLocation, etc.}
Adding Query Functions
A query function takes arguments, like query.my_charge_level(1). Use one when a single query needs a parameter, such as a slot, an axis or a threshold.
Step 1: Create the Function
Extend ContextQueryFunction. It gives you the current MolangQueryContext, which for AzureLib animators is an AzMolangQueryContext holding the entity being animated:
public class ChargeLevel extends ContextQueryFunction {public ChargeLevel(IValue[] values, String name) throws Exception {super(values, name);}@Overridepublic int getRequiredArguments() {return 1;}@Overrideprotected double evaluate(MolangQueryContext context) {if (context instanceof AzMolangQueryContext azContext && azContext.entity() instanceof MyEntity entity) {return entity.getChargeLevel(intArg(0));}return 0;}}
Amaran
Extend ContextQueryFunction (or keep your class out of the mod.azure.azurelib.core.molang.functions package). Functions in that package are treated as pure math and are folded to a constant when animations load, so a game-state query would freeze at its load-time value.
Step 2: Register the Function
Register it during mod initialization, before resources load, alongside your variables:
MolangParser.INSTANCE.registerFunction("query.my_charge_level", ChargeLevel.class);
Animations can then call query.my_charge_level(1) or q.my_charge_level(1). Unlike variable queries, functions are always written with parentheses.
Registration Methods
Memoized Values
The supplier runs at most once each time the value is bound: the first time an animation reads the query for that entity that frame. Every other read that frame reuses the result. Because you bind it again every frame, the value still updates every frame. This is the right choice for almost every query, especially ones that are expensive to calculate or used by several keyframes:
MY_CUSTOM_VALUE_REF.setMemoized(() -> calculateValue());private double calculateValue() {// Runs once per entity per frame, no matter how many keyframes read the querydouble baseValue = 10.0;double modifier = 1.5;return baseValue * modifier;}
Dynamic Values
The supplier runs every time an animation reads the query. Only use this when the value must be recalculated on each read rather than once per entity per frame:
MY_CUSTOM_VALUE_REF.set(() -> getUpdatedValue());private double getUpdatedValue() {// Runs on every read of the querydouble currentTime = System.currentTimeMillis() / 1000.0;return Math.sin(currentTime) * 10.0; // Creates a smooth oscillating value}
Static Values
For constant values:
MolangParser.INSTANCE.register(new LazyVariable(ModClass.MY_CUSTOM_VALUE, 42.0));
Using Your Queries in Animations
Queries can be written with either the full or the short prefix, so query.my_custom_value and q.my_custom_value are the same query. The same applies to variable./v., temp./t. and context./c..
Variables
Molang has three kinds of names you can store values in:
| Prefix | Lives | Typical use |
|---|---|---|
variable. / v. | On each animatable, across frames and across every expression | State an animation keeps, values driven from code |
temp. / t. | One evaluation of one expression; starts at 0 every time | Intermediate results inside a longer expression |
query. / q., context. / c., math., array. | Read-only | Game state, built-in functions and arrays |
Each entity, block entity and item has its own set of variable.* values, so a value one entity assigns is never seen by another. A value assigned in one keyframe is readable in every other keyframe of that animatable, on the same frame and later frames.
Assigning to a query, a context.* or math.* name, or a constant is an error: the expression logs a message and evaluates to 0.
Driving variable.* from Code
The simplest way to feed per-entity data to an animation, without registering a query, is to set a variable on the animatable's scope:
@Overrideprotected void applyMolangQueries(MyEntity entity, double animTime, float partialTicks) {super.applyMolangQueries(entity, animTime, partialTicks);this.context().molangScope().set("v.charge", entity.getChargeLevel());}
Animations read it as v.charge or variable.charge. The scope also has get, isSet, remove and clear.
Variables can hold strings, arrays and entity references as well as numbers. Set those with setObject:
this.context().molangScope().setObject("v.mode", entity.isAngry() ? "angry" : "calm");this.context().molangScope().setObject("v.skins", MolangArray.ofNumbers(0, 1, 2));
Entity references stored in a variable.* are held weakly, so a scope never keeps an entity alive. Once the entity is gone the variable reads as unset, and v.target ?? ... falls through.
Expression Syntax
Keyframe values can be a single expression or several statements separated by ;:
t.wave = math.sin(q.anim_time * 360);v.peak = math.max(v.peak ?? 0, t.wave);return t.wave * 10;
| Syntax | Meaning |
|---|---|
+ - * / % ^ | Arithmetic (^ is power). |
< <= > >= == != | Comparisons, giving 1 or 0. |
&&, ||, ! | Logic. && and || stop early, so the right side isn't evaluated when it can't matter. |
a ? b : c | b if a is non-zero, else c. |
a ? b | b if a is non-zero, else 0. |
v.x ?? 1 | v.x if it has been given a value, else 1. |
name = value | Assignment. Allowed anywhere an expression is; it evaluates to the assigned value. |
{ ...; ...; } | A block of statements, e.g. as the branch of a conditional. |
return value | Ends the expression with value. |
loop(count, { ... }) | Runs the block count times (at most 1024). break leaves the loop, continue skips ahead. |
'text' | A string. Case is kept; strings support only == and !=. |
array[index] | An array element. The index is floored, negative reads element 0, past the end wraps around. |
for_each(t.e, array, { ... }) | Runs the block once per element, assigned to t.e (or a v.*). Supports break/continue. |
entity->expression | Evaluates expression against another entity, e.g. t.e->q.health. 0 if it isn't a live entity. |
this | The current value of the bone channel the keyframe sets. 0 outside keyframes. |
true, false, math.pi | 1, 0 and π. |
A conditional with blocks:
q.is_on_ground ? { v.air_time = 0; } : { v.air_time = (v.air_time ?? 0) + 1; };return math.min(v.air_time, 20) * 2;
- Without a
return, a multi-statement expression evaluates to its last statement. - Names are case-insensitive; string contents are not.
- An expression the compiler can't parse is retried with AzureLib's older parser and logs a warning containing "Falling back to the legacy parser"; if that fails too, it evaluates to 0. Invalid Molang, such as
'a' + 1or assigning to an array element, is rejected outright and evaluates to 0.
Strings
q.get_equipped_item_name == 'bow'q.is_item_name_any('slot.weapon.mainhand', 0, 'minecraft:crossbow')q.equipped_item_any_tag('main_hand', 'minecraft:swords')q.any(q.get_equipped_item_name, 'bow', 'crossbow')
A string read as a number is 0.
Arrays and for_each
Arrays come from queries such as q.get_nearby_entities(radius, 'minecraft:pig'), from array.* names registered in code, or from a variable.* set with setObject. They are read-only and shared by reference.
t.babies = 0;for_each(t.pig, q.get_nearby_entities(8, 'minecraft:pig'), {t.babies = t.babies + t.pig->q.is_baby;});return t.babies;
q.count(array) gives the number of elements. q.get_nearby_entities scans the level each time it is called, so store the result in a t.* if you need it twice. Register an array.* with:
MolangParser.INSTANCE.registerArray("array.skins", () -> MolangArray.ofNumbers(0, 1, 2));
The arrow operator
entity->expression evaluates the right side as if animating that entity. Its queries read the other entity, and its variable.* values are that entity's own if it has an AzureLib animator. A removed or non-entity value on the left gives 0 without evaluating the right side.
this
In a keyframe, this is the value the bone channel currently holds, in the same units the keyframe uses: degrees offset from the bind rotation, position offset, or scale. That is the pose the bone finished the previous frame on, so this eases a bone toward 45° over several frames instead of snapping:
math.lerp(this, 45, 0.2)
Targets
q.has_target, q.target_x_rotation and q.target_y_rotation read the mob's attack target. Vanilla doesn't sync a mob's target to the client, so on most mobs these read 0 until you implement AzMolangTargetProvider on your entity and return a target you sync yourself (for example an entity id in SynchedEntityData). The rotations are absolute, like q.body_y_rotation; use math.min_angle(q.target_y_rotation - q.body_y_rotation) for a body-relative angle.
Items
While an item renders in an entity's hand, entity queries in its animations answer for the holder, like Bedrock attachables, and q.is_first_person is 1 when the item itself is drawn in a first-person hand.
Best Practices
- Always use
MolangQueries.normalize()when defining query constants - Define query constants as
public static finalfields - Register variables during initialization using
MolangParser.INSTANCE— never instantiate a newMolangParser - Use memoized values unless a query really needs recalculating on every read
- In animators, bind through a
MolangVariableRefand create suppliers once, reading the current entity from a field, instead of creating new lambdas every frame - Keep query names descriptive and consistent
- For simple per-entity values,
context().molangScope().set("v.name", value)avoids registering anything - Use
temp.*for scratch values inside an expression andvariable.*only for state that must persist or be shared between keyframes