MOLANG DOCUMENTATION Version: 1.9.0.10
Index#
Why Does MoLang Exist?#
MoLang is a simple expression-based language designed for fast calculation of values at run-time. Its focus is solely to enable script-like capabilities in high-performance systems where JavaScript is not performant at scale. We need scripting capabilities in these low-level systems to support end-user modding capabilities, custom entities, rendering, and animation.
Lexical Structure#
The language structure is largely based on simple C-style syntax, focusing on handling math expressions. A script is made of either one expression for simple cases, or can be made of several where intermediate values are required or to help reduce compute time.In all cases, the value of the last expression in a script provides the script's value. In a multi-expression script, all but the last expression must assign a value to a variable. The last expression can as well, but doesn't need to as it's value is assumed to be the used as the return value.
Values#
-Value types are concretely specified, or numeric if not.-All numerical values are floats.-Boolean values such as actor flags are converted to a float value of either 0.0 or 1.0 for values of false or true respectively.-For boolean tests, a float value equivalent to 0.0 is false, and anything not equal to 0.0 is true.-For array indices, floats are c-style cast to ints, and clamped at zero for negative values or wrapped by the array size for large values.-Other supported types are textures, materials, and geometry where they make sense (such as render controllers).-Errors generally return a value of 0.0.
Variables#
There are several domains a variable may belong to:
Parameters
Domain | Scope | Example |
Temp |
Current expression |
Temp.foo = Math.sin(global.anim_time);return Temp.foo * Temp.foo; |
geometry |
Current render controller |
"geometry": "array.geos[query.is_sheared]" |
material |
Current render controller |
"materials": [ { "*": "material.default" }, { "leg*": "material.legs" } ] |
query |
Read-only values, usually related to current entity |
query.is_baby |
texture |
Current render controller |
"textures": ["array.skins[query.is_saddled]"] |
variable |
Current entity |
variable.my_saved_var = variable.my_saved_var + 1; |
Keywords#
All identifiers not in a scope listed below are reserved for future use
Parameters
Keyword | Description |
|
Numerical constant value |
( ) |
Parentheses for expression term evaluation control |
|
Brackets for array access |
query. |
Access to an entity's properties |
math. |
Various math functions (see below) |
global. |
Access to environment parameters |
temp. |
Store an intermediate value for the duration of the current expression |
variable. |
Store a value on the attached entity for later use |
geometry. |
A reference to a texture named in the entity definition |
material. |
A reference to a texture named in the entity definition |
texture. |
A reference to a texture named in the entity definition |
! && || < <= >= > == != |
Logical operators |
* / + - |
Basic math operators |
? : |
Conditional operator |
this |
The current value before executing the script (context sensitive) |
return |
For complex expressions, this evaluates the following statement and stops execution of the script, returns the value computed |
Math Functions#
Parameters
Function | Description |
math.abs() |
Absolute value of |
math.sin() |
Sine (in degrees) of |
math.cos() |
Cosine (in degrees) of |
math.exp() |
Elevates Euler to the 'd power |
math.ln() |
Natural logarithm of |
math.pow(, ) |
Elevates to the 'd power |
math.sqrt() |
Square root of |
math.random(, ) |
Random value between and inclusive |
math.ceil() |
Round up to nearest integral number |
math.round() |
Round to nearest integral number |
math.trunc() |
Round towards zero |
math.floor() |
Round down to nearest integral number |
math.mod(, ) |
Return the remainder of value / denominator |
math.min(, ) |
Return lowest value of or |
math.max(, ) |
Return highest value of or |
math.clamp(, , ) |
Clamp to between and inclusive |
math.lerp(, , <0_to_1>) |
Lerp from to via <0_to_1> |
math.lerprotate(, , <0_to_1>) |
Lerp the shortest direction around a circle from degrees to degrees via <0_to_1> |
Global Parameters#
Parameters
Name | Description |
global.anim_time |
Time since the current animation start |
global.delta_time |
Time since the last time this animation was updated |
global.key_frame_lerp_time |
Normalized time between current key frames as[0 - 1] |
global.life_time |
Lifetime of the current entity |
global.frame_alpha |
The render frame interpolation amount between AI frames(assuming the rendering is running at a different rate than the AI) as[0 - 1] |
Types, Values, and Variables#
In general, all expression values are floats. In render controllers, some expressions result in a texture or material depending on the context. All array index expressions are processed as floats, and cast to integers when performing the final lookup into the array.Positive array indices wrap by the size of the array.Negative array indices clamp to 0.
Simple vs Complex Expressions#
A simple expression is a single statement, the value of which is returned to the system that evaluated the expression. eg:
math.sin(global.anim_time * 1.23)
A complex expression is one with multiple statements, each ending in a ';'. Each statement is evaluated in order. In the current implementation, the last statement requires the use of the return keyword and defines the resulting value of the expression.eg:
temp.my_temp_var = Math.sin(global.anim_time * 1.23);
temp.my_other_temp_var = Math.cos(global.life_time + 2.0);
return temp.my_temp_var * temp.my_temp_var + temp.my_other_temp_var;
Note that in a simple expression, ';' is not allowed, whereas in a complex expression, each statement requires a ';' including the last.
Domain Examples#
Entity Definition Scripts#
In the definition file there is a section for pre-computing values. These are executed immediately before animation and render controllers are processed, and stored in the entity. The purpose is to pre-compute any expensive and complex values you may want to reuse in your scripts, long-living index variable updates, or generally any one-off computation per render tick.
"scripts": {
"pre_animation": [
"variable.my_constant = (Math.cos(global.anim_pos * 38.17) * global.anim_speed;",
"variable.my_constant2 = Math.exp(1.5);",
]
},
Animation and Animation Controller Files#
These are always numerical operations to control which animations are playing and how to animate bones. "variable.
" and "query." refers to the entity currently being rendered.They have access to everything in the language except material, texture, and geometry types.
Render Controllers#
There are a few different kinds of expressions here, where context implies what is allowed. As with animations, the entity accessors refer to the current entity, however depending on the context one also has access to materials, textures, and geometries.There are two sections in a render controller:-Array definitions (optional)-Resource usage (required)The array definition section allows you to create arrays of resources by resource type if you so desire.These can then be referenced in the resource usage section.Parameters
{
"controller.render.<my render controller name>": {
"arrays": {
"materials": {
"Array.<array name>": ["Material.<material name>", "Array.<array name>, ...],
...
},
"textures" : {
"Array.<array name>": ["Texture.<texture name>", "Array.<array name>", ...],
...
},
"geometries" : {
"Array.<array name>": ["Geometry.<geometry name>", "Array.<array name>", ...],
...
}
},
"geometry": "<geometry expression>",
"materials" : [
{ <string regex>: "<material expression>" },
...
],
"textures" : [
"Array.<array name>[<numerical expression>]",
...
]
}
}
Array Expressions#
For each of the three resource types (materials, textures, and geometry), you can define an array of resources. The name of the resource is the nice-name from the definition file. Using materials as an example:
"arrays": {
"materials": {
"array.my_array_1": ["material.a", "material.b", "material.c"],
"array.my_array_2" : ["material.d", "material.e"],
"array.my_array_3" : ["array.my_array_1", "material.my_array_2"],
"array.my_array_4" : ["array.my_array_2", "material.my_array_3"],
"array.my_array_5" : ["array.my_array_1", "material.my_array_1", "material.my_array_4"],
"array.my_array_6" : ["array.my_array_1", "material.f"],
...
},
Note that all elements of an array must be of the same type. eg: a texture array must only contain textures.An array can reference any combination of zero or more arrays (including duplicates if desired) and/or zero or more materails (again, including duplicates if you like), and you can have as many arrays as you like, each with as many elements as you like.If an array includes arrays in its members, they do not need to be the same length.When indexing into an array in the resource usage section, you use numerical expressions.If the resulting number is negative, it will use zero as the index.Any non - negative index will converted to an integer, and will wrap based on the size of the array:
index = max(0, <expression_result>) % <array_size>
Resource Expression#
A resource expression must return a single resource of a specific type depending on the context. For example, in the "geometry" section, you must produce an expression that will result in a single geometry. Some examples:
Always use a specific geometry
"geometry": "geometry.my_geo"
Cycle through an array of geometries at a rate of one per second
"geometry": "array.my_geometries[global.anim_time]"
Pick a geo based on an entity flag
"geometry": "query.is_sheared ? geometry.sheared : geometry.woolly"
Use specific geo when sleeping, otherwise flip through an array based on a cosine curve, using index zero for almost half the time while the cosine curve is negative
"geometry": "query.is_sleeping ? geometry.my_sleeping_geo : array.my_geos[math.cos(global.anim_time * 12.3 + 41.9) * 10 + 0.6]"
Resource Sections#
Geometry#
The geometry section specifies which geometry to use when rendering. As you can specify as many render controllers as you like in the definition file, a single render controller is only concerned with how to render a single geometry.Note that a geometry can be arbitrarily complex using any number of bones and polygons.
Materials#
The materials section specifies how to map what material to what bone of the geometry. A single material is mapped to a whole bone. Material expressions are evaluated in the order listed. The first part of each statement is the name of the model part to apply the material to, and the second part is the material to use. The model part name can use * for wild - card matching of characters.For example :
"materials": [
{ "*": "Material.default" },
{ "TailA": "array.hair_colors[variable.hair_color]" },
{ "Mane": "array.hair_colors[variable.hair_color]" },
{ "*Saddle*": "variable.is_leather_saddle ? material.leather_saddle : material.iron_saddle" }
],
-This will start by applying Material.default to all model parts.-Next, it will set the material on a model part named "TailA" to the result of the expression "Array.hairColors[variable.hair_color]". This will look up some previously created variable on the entity named hair_color and use that to index into a material array called "array.hair_colors" defined in this render controller. This will overwrite the Material.default material set in the line above.-Third, it will look up the same material as the expression is identical, and apply it to the "Mane" model part.-Lastly, if will find any model part starting with, ending with, or containing "Saddle" (case sensitive) and change its material to either material.leather_saddle or material.iron_saddle depending on the previously set entity variable variable.is_leather_saddle.
Query Functions#
Query Functions are boolean expressions that allow you to query for values owned by the engine under different circumstances. They can be used in MoLang expressionsUseful for controlling things like changing positions, textures, animations, etc if a mob is a baby. For example:
"position": [ 0.0, "query.is_baby ? -8.0 : 0.0", "query.is_baby ? 4.0 : 0.0" ]
List of Entity Flags#
Name | Description |
query.can_climb |
Mob is able to climb |
query.can_damage_nearby_mobs |
Set by the player's spin attack using the Trident |
query.can_fall_through_scaffolding |
Set when the player is able to fall through scaffolding, for example when flying or descending in scaffolding |
query.can_fly |
Mob is able fly |
query.can_power_jump |
Mob can power jump(added with the minecraft : can_power_jump component |
query.can_rider_pick |
This is set by the "rider_can_interact" Parameter of the minecraft : rideable component. |
query.can_show_name |
This flag is set is the entity can show a name tag |
query.can_swim |
Mob is able to swim |
query.can_walk |
Mob is able to walk |
query.ground_speed |
Entity speed in meters |
query.is_angry |
Mob is currently angry(i.e.wolves that been hit, zombie pigman once hit).Added via the minecraft : angry component |
query.is_baby |
Mob is a baby |
query.is_breathing |
if false, HUD bubbles render(water only) |
query.is_bribed |
Added when the mob is bribed(added through the minecraft : bribable component |
query.is_casting |
Mob is casting a spell. (i.e.Evoker animation of arms in air and to have particles play next to its hands) |
query.is_charged |
This is set on a mob by the charge_charged_trigger parameter on the minecraft : behavior.ranged_attack component(i.e.the blaze's fireball attack) |
query.is_charging |
Mob is using a charge attack(i.e.the vex's charge attack) |
query.is_chested |
If the Mob has a chest on them(i.e.Horses and Donkey chests) |
query.is_critical |
When a critical attach is achieved(i.e.when the player firs a fully charged bow or gets a critical melee attach this causes the particles to play |
query.is_dancing |
When a mob is dancing(i.e.used by parrots to indicate they should dance) |
query.is_eating |
If the mob is eating(i.e.When horses are fed hay or pandas eating) |
query.is_elder |
If the Mob is an elder version(only used on Elder Guardian) |
query.is_fire_immune |
If the mob has minecraft : fire_immune component or the fire resistance potion applied |
query.is_gliding |
Player is gliding(using elytra) |
query.is_idling |
Used when the Agent is idling |
query.is_ignited |
If a mob is ignited.Added through the is_ignited and explode components |
query.is_in_love |
Mob is in its "Love" state by being fed it's tempt item |
query.is_in_scaffolding |
Managed by the minecraft : scaffolding_climber component for when a mob is in scaffolding |
query.is_in_transition_sitting |
If the mob is currently transitioning from sitting or standing(i.e.Pandas when randomly sitting or snacking) |
query.is_in_water |
Is entity in water |
query.is_interested |
Mob is attracted to food the player is holding food they like(added via the minecraft : behavior.beg.) (i.e.tamed wolf sits and turn its head) |
query.is_invisible |
Mob is invisible / affected by Invisibility potion effect |
query.is_laying_down |
Managed by the minecraft : behavior.lay_down goal(i.e.lazy pandas) |
query.is_laying_egg |
If the turtle is laying an egg |
query.is_leashed |
Mob currently has a leash on it. |
query.is_lingering |
Used on potions that are thrown to know it creates a lingering effect. |
query.is_moving |
Mob is currently swimming through water |
query.is_onfire |
Mob is on fire |
query.is_orphaned |
Pets that have lost their owners. |
query.is_over_scaffolding |
Managed by the minecraft : scaffolding_climber component for when a mob is over scaffolding |
query.is_powered |
If mob is charged(i.e.Creeper's blue shield). Added via minecraft:charged component |
query.is_pregnant |
Set by some turtle behavior goals.Used to make pregnant turtles render larger |
query.is_resting |
Pet sleeping with its owner or boat is resting |
query.is_riding |
Player / Mob is riding another Mob |
query.is_rolling |
Set by the minecraft : behavior.roll goal(i.e.baby or playful pandas) |
query.is_saddled |
Mob has a saddle on its back(i.e.pigs and horses) |
query.is_scared |
Managed by the minecraft : behavior.scared goal for when the mob is scared(i.e.scared pandas) |
query.is_shaking |
When Mobs shake off water(i.e.Wolves shaking off water when wet from swimming / rain |
query.is_sheared |
Mob has been sheared.Set with the minecraft : sheared component |
query.is_sitting |
Mob is currently sitting(i.e.dogs and cats) |
query.is_sneaking |
Mob is Sneaking(i.e.players and ocelots) |
query.is_sneezing |
Set by the minecraft : behavior.sneeze goal when the mob is sneezing |
query.is_sprinting |
Mob is Sprinting |
query.is_stackable |
Used on minecarts and boats so that they don't collide through each other. Set via minecraft:stackable component |
query.is_standing |
Mob is stomping(i.e.zombie's minecraft:behavior.stomp_turtle_egg, or Horses and the player jumping ) |
query.is_swimming |
True if the Mob is currently swimming |
query.is_tamed |
If Mob is currently tamed(i.e.Dogs use this to draw their collars) |
query.is_tempted |
Mob is tempted by a food item |
query.is_transforming |
This flag is set by the minecraft : transformation component(i.e.Zombie villagers when they are transforming back to villagers) |
query.is_using_item |
Mob is using an item(i.e.players eating food, or players and witches using potions) |
query.is_wall_climbing |
Mob is currently climbing a wall(i.e.spiders) |
query.is_wasd_controlled |
When the mob is being controller by a player riding it |