Lua Scripting: Behavior Programs
A behavior program replaces a mob's native AI with Lua behaviors that decide where it moves, what it looks at, which entity it targets and when it attacks. EliteMobs runs these programs on Minecraft's native Brain system through MagmaCore's Mind runtime.
Behavior programs are not Lua powers. A Lua power reacts to boss events such as on_boss_damaged_by_player; a behavior program runs every tick and owns the mob's movement controls. A boss can use both: a behavior can ask the boss's powers to perform an action through on_mind_action.
Set behavior in a custom boss file, as described in Creating Bosses, or in the entity type's mob-properties file. behavior: native keeps vanilla AI. If the server version has no native Mind support, EliteMobs logs Native Mind interface is unavailable on this Minecraft version. at startup. A custom boss that selects a program then does not spawn and logs Cannot spawn <boss file>: .... A boss whose behavior names a missing or invalid program fails the same way on any version.
Files
Behavior files live in plugins/EliteMobs/behaviors/. EliteMobs writes these bundled files when they are missing:
plugins/
EliteMobs/
behaviors/
basic_melee.lua
modules/
target.lua
pursuit.lua
melee.lua
wander.lua
- A
.luafile inside any folder namedmodulesis a module. Every other.luafile is a program. - A boss references a program by its path relative to
behaviors/, for examplebasic_melee.luaorguards/patrol_guard.lua. - EliteMobs loads behavior files when its Mind service starts. A file that fails validation is skipped and the console logs
Could not load behavior <file>orCould not load behavior module <file>with the reason. - Program and module ids use the
elitemobsnamespace, such aselitemobs:behavior/basic_melee. Ids are lowercase and may containa-z,0-9,.,_and-, plus/after the colon. Two programs cannot share an id, and each module id should be declared by one file. - One namespace holds at most 256 modules. A module can list at most 32 dependencies, a program can resolve at most 64 modules, and each source file is limited to 1,000,000 characters.
The same sandbox rules as other Lua scripts apply. See the Lua Sandbox. Each mob that runs a program gets its own Lua environment, so file-local variables are not shared between mobs.
Program File
A program file returns ai.program { ... }:
return ai.program {
id = 'elitemobs:behavior/basic_melee', revision = 1,
modules = {
'elitemobs:behavior/target', 'elitemobs:behavior/pursuit',
'elitemobs:behavior/melee', 'elitemobs:behavior/wander'
},
budget = {
callback_micros = 2000, entity_micros = 4000, server_micros = 5000,
max_callbacks = 20, max_instructions = 12000, max_action_requests = 2
}
}
| Field | Type | Notes |
|---|---|---|
id | string | Required. Namespaced id, elitemobs:... for files in behaviors/. |
revision | positive integer | Required. |
modules | array of strings | Optional. Module ids whose memories, sensors and behaviors join the program. Dependencies load before the modules that need them. Duplicates and dependency cycles are rejected. |
memories, sensors, behaviors | tables | Optional. A program can declare its own, in the same format as a module. |
budget | table | Optional soft scheduling limits. See Budgets. |
runaway | table | Optional hard per-callback limits. See Budgets. |
Module File
A module file returns ai.module { ... } and groups reusable memories, sensors and behaviors:
| Field | Type | Notes |
|---|---|---|
id | string | Required. Namespaced module id. |
revision | positive integer | Required. |
dependencies | array of strings | Optional. Other module ids this module needs. |
memories | table | Optional. See Memories. |
sensors | array | Optional. ai.sensor { ... } entries. |
behaviors | array | Optional. ai.behavior { ... } entries. |
sensors, behaviors, modules and dependencies must be plain arrays without gaps or named keys. Sensor, behavior and memory names must be unique across every module in a program.
Memories
Memories are typed values that sensors and behaviors share for one mob. Declare each one by name:
memories = {
candidate = { type = 'uuid', persistent = false },
next_attack = 'integer'
}
| Type | Lua value |
|---|---|
string | string |
boolean | boolean |
integer | whole number |
number | number |
uuid | UUID string |
position | { world = 'world', x = 0, y = 64, z = 0 } |
A name without a colon is placed in the program's namespace. persistent defaults to false; true marks the memory for serialization with the Mind's saved state. Read and write memories through c.memory:get(name), c.memory:set(name, value, ttl_ticks), c.memory:forget(name) and c.memory:contains(name). The third set argument is optional; when supplied, the value expires after that many ticks. Using an undeclared name raises an error.
Sensors
A sensor gathers information, usually into memories. Sensors run before behaviors.
ai.sensor {
id = 'elitemobs:behavior/find_target', interval = 20,
sense = function(c)
local target = c.perception:nearest_player(35)
if target then c.memory:set('candidate', target.uuid, 21)
else c.memory:forget('candidate') end
end
}
| Field | Type | Default | Notes |
|---|---|---|---|
id | string | Required. | |
interval | positive integer | 1 | Ticks between runs. |
sense | function | Required. Receives the Mind context. |
Sensors cannot use c.actuator; calling an actuator method from a sensor raises an error.
Behaviors
A behavior acts on the mob while it holds the controls it declares.
ai.behavior {
id = 'elitemobs:behavior/attack', priority = 10,
controls = { ai.controls.attack },
can_start = function(c) return c.perception:current_target() ~= nil end,
can_continue = function(c) return c.perception:current_target() ~= nil end,
tick = function(c)
local target = c.perception:current_target()
if target then c.actuator:attack(target) end
end
}
| Field | Type | Default | Notes |
|---|---|---|---|
id | string | Required. | |
priority | integer | 0 | Lower numbers take precedence. |
controls | array | none | Controls this behavior leases while it runs. |
can_start | function | always true | Must return true or false. |
can_continue | function | same as can_start | Must return true or false. |
start | function | none | Runs once when the behavior starts. |
tick | function | Required. Runs every tick while the behavior runs. | |
stop | function | none | function(c, reason); runs when the behavior stops. |
Lifecycle
- While stopped, the behavior checks
can_start. When it returnstrueand the behavior can lease every declared control,startruns. - On every tick while it runs, including the tick it started,
can_continueruns first. When it returnsfalse, the behavior stops with reasoncompleted; otherwisetickruns. stop(c, reason)receives one ofcompleted,preempted,program_replaced,entity_removed,callback_failedorhandle_closed. Stopping releases the behavior's controls and halts the movement, target or attack it held.
A Lua error in can_continue, start or tick stops the behavior with callback_failed. Returning anything other than a boolean from can_start or can_continue is an error. The console logs Mind <program> callback <callback> failed with ... for each failure.
Controls
| Control | Allows |
|---|---|
ai.controls.move | c.actuator:move_to(...), c.actuator:stop_moving() |
ai.controls.look | c.actuator:look_at(...) |
ai.controls.jump | c.actuator:jump() |
ai.controls.target | c.actuator:set_target(...), c.actuator:clear_target() |
ai.controls.attack | c.actuator:attack(...) |
ai.controls.action | c.actions:request(...) |
ai.controls.use_item | Reserved; the Lua actuator has no item method. |
Each control belongs to one running behavior at a time. A behavior with a lower priority number takes a control from a behavior with a higher number, which stops with reason preempted. It cannot take a control held by a behavior with a lower number. Between equal priorities, the behavior whose id sorts first alphabetically wins the control. Calling an actuator method without holding its control raises an error. c.actuator:stop_all() stops only what the behavior holds.
In the bundled modules, target selection uses priority 5, melee attacks 10, pursuit 20 and idle wandering 50, so pursuit takes movement from wandering as soon as a target exists.
Requesting boss actions
A behavior holding ai.controls.action can call c.actions:request(identifier, payload). EliteMobs sends the request to the boss's Lua powers through on_mind_action. The identifier must be a lowercase namespaced key of at most 128 characters, such as 'elitemobs:slam'. The payload holds at most 16 entries with lowercase keys of at most 64 characters, and string values are limited to 256 characters; an invalid identifier or payload raises an error. The call returns accepted, deferred or rejected; requests beyond the program's max_action_requests in one tick return deferred. See the Mind action context for the payload format.
Budgets
budget sets soft scheduling limits. When a mob reaches one in a tick, the Mind runtime skips that mob's remaining callbacks until the next tick. A callback that is already running always finishes.
| Key | Default | Notes |
|---|---|---|
callback_micros | 2000 | A callback that runs longer than this ends the mob's callbacks for the tick. |
entity_micros | 4000 | Total callback time per mob per tick. |
server_micros | 20000 | The mob's callbacks stop for the tick once all Mind programs together have used this much time. |
max_callbacks | 128 | Callbacks per mob per tick. |
max_action_requests | 8 | Action requests per mob per tick; at most 64. |
All values must be positive, and callback_micros cannot exceed entity_micros, which cannot exceed server_micros.
runaway sets hard limits that interrupt a single callback:
| Key | Default | Notes |
|---|---|---|
cpu_micros | 50000 | Thread CPU time per callback. |
max_instructions | 50000 | Lua instructions per callback. |
budget also accepts max_instructions, as in the bundled basic_melee.lua. Declare it in budget or runaway, not both. A callback that exceeds a runaway limit fails like any other callback error.
Next Steps
- Creating Bosses: behavior -- selecting a program for a boss
- Lua API Reference: Mind program context -- every
c.memory,c.perception,c.actuatorandc.actionsmethod - Hooks & Lifecycle --
on_mind_actionand the other Lua power hooks
