data-driven event system
1# Expressions
2
3Commander introduces support for evaluating math expressions with dynamic context data.
4
5Expressions use [EvalEx](https://ezylang.github.io/EvalEx/) for evaluation, it may be useful to familiarize yourself with it. One major difference is that all function, variable, contant names are case-sensitive and all funtion names are camelCase and not UPPER_SNAKE_CASE.
6
7## Additional functions and operators
8
9Commander adds some additional functions to EvalEx.
10
11A "Lambda" here means a regular expression, but with the `it` parameter. Example: `arrayFind(arrayOf(0, 1, 2), it == 1)`. Lambda parameters are marked with `λ`.
12
13Variable Arguments (VarArgs) are marked with `...`. (This means you can specify as many arguments as you need)
14
15::: details Math
16
17| Function | Description | Arguments | Example |
18|---|---|---|---|
19| `random` | Generates a random number in range. | `min`, `max` | `random(0, 23)` |
20| `clamp` | Clamps the value between two other arguments. | `value`, `min`, `max` | `clamp(12, 14, 16)` |
21| `lerp` | Smoothly progresses the value to the target. | `delta`, `start`, `end` | `lerp(0.5, 10, 16)` |
22
23:::
24
25::: details Arrays
26
27All functions construct new arrays and do not mutate the original array.
28
29| Function | Description | Arguments | Example |
30|---|---|---|---|
31| `arrayOf` | Construct an array from the specified objects. | `args...` | `arrayOf(0, 23)` |
32| `arrayMap` | Mutates all objects in this array. | `array`, `function(λ)` | `arrayMap(arrayOf(0,1,2), sqrt(it))` |
33| `arrayFind` | Filters all objects not matching the predicate. | `array`, `predicate(λ)` | `arrayFind(arrayOf(0,1,2), it == 1)` |
34| `arrayAnyMatch` | Checks if any of the objects in this array match the predicate. | `array`, `predicate(λ)` | `arrayAnyMatch(arrayOf(0,1,2), it == 1)` |
35| `arrayNoneMatch` | Checks if none of the objects in this array match the predicate. | `array`, `predicate(λ)` | `arrayNoneMatch(arrayOf(0,1,2), it == 1)` |
36| `arrayAllMatch` | Checks if all objects in this array match the predicate. | `array`, `predicate(λ)` | `arrayAllMatch(arrayOf(0,1,2), it == 1)` |
37| `arrayFindFirst` | Returns the first object in the array or `null` if empty. | `array` | `arrayFindFirst(arrayOf(0, 23))` |
38
39:::
40
41::: details Registries
42
43| Function | Description | Arguments | Example |
44|---|---|---|---|
45| `Registry` | Returns a static content registry | `identifier` | `Registry('entity_type')` |
46| `Item` | Returns an item from the registry | `identifier` | `Item('diamond')` |
47| `Block` | Returns a block from the registry | `identifier` | `Block('chest')` |
48| `DynamicRegistry` | Returns a dynamic content registry | `identifier` | `DynamicRegistry('worldgen/biome')` |
49| `Biome` | Returns a biome from the registry | `identifier` | `Biome('the_void')` |
50| `DimensionType` | Returns a dimension type from the registry | `identifier` | `DimensionType('overworld')` |
51
52:::
53
54::: details Misc
55
56| Function | Description | Arguments | Example |
57|---|---|---|---|
58| `structContainsKey` | Checks if a struct contains some key. | `struct`, `key...` | `structContainsKey(block_state.properties, 'candles')` |
59| `hasContext` | Checks if an expression parameter is available | `key...` | `hasContext('tool')` |
60| `length` | Returns the length of the specified object or 0. | `value` | `length('Hello World!')` |
61| `strFormat` | Formats a string according to the pattern. | `pattern`, `args...` | `strFormat('Hello %s World!', 23)` |
62| `ifMatches` | Simillar to built-in `if`, but with Lambdas. | `value`, `predicate(λ)`, `ifTrue(λ)`, `ifFalse(λ)` | `ifMatches(arrayFind(arrayOf(0,1,2), it == 1), length(it) > 0, it[0], 0)` |
63| `chain` | Chains multiple functions using lambdas. `it` is the last result. | `value`, `actions(λ)...` | `chain(23, it + 3, sqrt(it))` |
64
65:::
66
67::: details Operators
68
69| Function | Description | Arguments | Example |
70|---|---|---|---|
71| `?.` | Attempts to get a field from a structure or returns `null` | `struct`, `key` | `struct?.field` |
72| `?` | Returns `b` if `a` is `null`. `a` otherwise. | `a`, `b` | `struct?.y ? 5` |
73
74:::
75
76## Context data access
77
78Because Expressions require minecraft's loot context, you can read source data using a special syntax.
79```
80identifier.field
81identifier.method
82identifier.method.field.method
83```
84Keep in mind that requested fields and context **must** be present. Expression will fail without them.
85
86Some examples:
87```
88minecraft:this_entity.getX
89this_entity.getHealth
90minecraft:this_entity.isInWaterOrRain
91this_entity.blockPosition.getX
92origin.x
93origin.reverse.y
94minecraft:level.getDayTime
95```
96
97### Commander Extensions:
98
99Commander adds special fields to objects to extend expression capabilities.
100
101::: details `nbt` (Stacks, Entities, Block Entities)
102
103Added to item stacks, entities, and block entities. Allows you to read the NBT of an object. While this can be convenient, NBT access is not fast, and when used in frequently invoked places (like tick events), can bring the game to a halt.
104
105Example: `this_entity.nbt.Air`
106
107:::
108
109::: details `properties` (Block States)
110
111Added to states, like block states.
112
113Example: `block_state.properties.candles`
114
115:::
116
117::: details `attributes` (Living Entities)
118
119Allows you to access entity attributes. The key is an identifier, so both `generic.luck` and `minecraft:generic.luck` are acceptible.
120
121Example: `this_entity.attributes.'generic.luck'`
122
123:::
124
125::: details `storage` (Levels, Chunks, Entities, Block Entities)
126
127Allows you to access persistent data storage. The data is modified and written by [`/cmd:data`](/BrigadierCommands#cmd-data)
128
129:::
130
131### P.S.
132
133If you have to do a lot of expensive operations in expressions in tick events, you should delay the execution, if possible, by checking `level.getDayTime % 20 == 0`, this will make sure that the expression is executed every second and not 20 times per second. `% 40` is every 2 seconds and `% 10` is every 1/2 second.
134
135Expressions use Mojang Mappings (downloaded on first load), this means that almost all public fields and getter methods are available in expressions. If Commander fails to setup mappings, you'll have to rely on platform names. (e.g. intermediary on Fabric)
136
137::: details Random examples
138
139Default minecart speed is computed using this formula:
140
141`if(this_entity.isInWater, 4, 8) / 20`, and furnace: `if(this_entity.isInWater, 3, 4) / 20`
142
143***
144
145This ridicilous expression can print the current level time in human time. https://bukkit.org/threads/how-can-i-convert-minecraft-long-time-to-real-hours-and-minutes.122912/
146
147`strFormat('%02.0f:%02.0f', floor((level.getDayTime / 1000 + 8) % 24), floor(60 * (level.getDayTime % 1000) / 1000))`
148
149So the output can be: `00:00`, or `13:45`, etc.
150
151:::
152
153## Using Expressions
154
155The fastest way to get started is to use the `cmd:arithmetica` command. You should wrap your expression with `"` to satisfy Brigadier.
156
157Other places you can use expressions in:
158
159::: details JSON Command Macros
160
161Command Macros are explained in detail on the Commands page. [Link](Commands#command-macros)
162
163:::
164
165::: details Loot number provider
166
167Using the `commander:arithmetica` provider in conditions:
168
169```json
170{
171 "condition": "minecraft:value_check",
172 "value": {
173 "type": "commander:arithmetica",
174 "value": "round(random(5, 15))"
175 },
176 "range": {
177 "min": 12.3,
178 "max": 32
179 }
180}
181```
182
183:::
184
185::: details Loot predicate
186
187Using the `commander:expression` predicate:
188
189```json
190{
191 "condition": "commander:expression",
192 "value": "level.isDay"
193}
194```
195
196:::
197
198::: details `execute` command
199
200You can use expressions as predicates in the `execute if` command.
201
202```
203/execute if cmd:expression "level.isDay" run say It is day!
204```
205
206:::
207
208::: details `scoreboard players` command
209
210You can use expressions as modifiers for player predicates. `score` variable allows you to get the current score.
211
212The current version does not provide entity context.
213
214```
215/scoreboard players cmd:operate @s test_objective "(score * 1.5) + level.storage.my_score_modifier"
216```
217
218:::