Repository navigation
API Entity Lifecycle and Data
Lua API / Entities / Entity lifecycle and data
Entity lifecycle settings control which simulation path runs. Declared data persists with an instance, while memory holds bounded transient state for the current loaded instance.
lifecycle = "native" (default) retains the kind's normal tick behavior. Set lifecycle = "manual" and provide onTick to take over ticking entirely. Manual lifecycle skips native movement, native living AI and environmental updates, projectile flight and expiry, and pickup movement, expiry, and automatic collection. Declared AI routines run before onTick; other callbacks and external interactions still run. Lua can move, rotate, damage, heal, or remove the entity through its handle. physics.mode affects native ticking; Manual lifecycle controls movement directly with setPosition or by switching back to native lifecycle in a later declaration reload. A manually controlled pickup can call entity:collect(player) when its own collection condition is met.
local turret = betamoon.entities:add {
key = "mymod:entity/turret",
kind = "living",
lifecycle = "manual",
appearance = { model = "mymod:entity/turret", texture = "mymod:entity/turret" },
onTick = function(ctx)
local player = ctx.world:getClosestPlayer(12)
if player then
ctx.entity:faceToward(player, 8, 8)
end
end
}The optional data table declares up to 64 fields by lowercase identifier of at most 64 characters. Primitive types are boolean, integer, number, and string; their defaults are false, 0, 0, and "". Strings are limited to 1,024 characters. vector stores a finite {x, y, z} value and defaults to zero. All fields are saved. save = true may be explicit.
list requires an element field definition. maxLength defaults to 64 and accepts 1-256. record requires a fields table with at most 64 nested definitions. Lists and records may nest up to four levels. A field schema may allow at most 4,096 stored values and one entity declaration at most 16,384, based on declared list bounds. Values returned by get are detached; edit them and call set to save the changes.
data = {
patrol = {
type = "list",
maxLength = 8,
element = { type = "vector" }
},
status = {
type = "record",
fields = {
mode = { type = "string", default = "idle" },
attempts = { type = "integer" }
}
}
}item_stack stores an item, count, and damage and defaults to nil when omitted. Its declaration may provide a registered stack as default. Set it with an item reference, ID, or stack table; nil clears it. Its returned table has id, count, damage, and available. A temporarily missing item ID remains saved and inspectable with available = false.
entity_reference stores a stable reference to a player or BetaMoon entity. Set it from a live entity handle or clear it with nil. get returns kind, identity, and token, plus isLoaded() and resolve(). resolve() returns a scoped entity handle only while the target is loaded, so cross-chunk references retain identity without loading terrain. Native or modded entities without a BetaMoon saved identity cannot be stored by this field.
Use ctx.entity.data:get(name) and :set(name, value) within callbacks. The methods validate types and expire after the callback. If a definition is temporarily missing, saved instances stay dormant with their complete data and reactivate when a compatible definition returns. Unknown top-level saved fields remain intact for later recovery. Changing a field type, a nested incompatible type, or reducing a list bound on reload is rejected atomically. See Entity Data.
ctx.entity.memory:get(name) and :set(name, value) hold transient values for one entity instance, such as a chosen route or a short-lived AI state. Memory is never saved. Names are lowercase identifiers of at most 64 characters; up to 64 fields are allowed. Values may be booleans, finite numbers, strings up to 1,024 characters, or tables up to four levels deep and 256 total entries. Tables are copied on read and write, so editing a returned table requires another set call. Passing nil clears a field. Callback handles, functions, and userdata cannot be stored.
onInteract(ctx) receives entity, player when available, and world when available. Return betamoon.callbackResults.handled to consume the interaction, .deny to suppress it, or .pass/nil for normal item fallback. The equivalent strings remain accepted. onTick(ctx) runs gameplay logic every tickInterval ticks (default 1, range 1–1200); it receives entity, world, and age. Errors disable the failing callback and are reported. Entity access and movement lists live queries and actions.
onSpawn(ctx) runs once after a new instance joins the world; onActivate(ctx) follows with reason = "spawn". A saved instance calls onLoad(ctx) once when a compatible definition is available, followed by onActivate(ctx) with reason = "chunk_load" or "definition_restored". An active instance calls onDeactivate(ctx) when its chunk unloads ("chunk_unload"), simulation pauses because nearby chunks are unavailable ("simulation_paused"), or it is removed. Simulation resumption calls onActivate(ctx) with "simulation_resumed". These activation callbacks run once per transition, independently of onTick and lifecycle mode. A missing definition cannot run its old callback; the instance keeps its saved data until the definition returns.
onDeath(ctx) runs once after fatal damage or entity:kill(). It includes attacker when known. onRemove(ctx) runs once when the entity is marked dead, after onDeactivate if active. Its reason can be "explicit", "death", "impact", "expired", "collected", "destroyed", "despawned", "void", "invalid_motion", "unloaded_chunk", or "removed". All lifecycle callbacks receive entity, world, and age; activation, deactivation, and removal callbacks also receive reason. Full pickup collection calls onPickup before removal callbacks. Chunk unload alone does not call onRemove.
Any entity kind can declare behavior with named states, timer callbacks, or both. States are lowercase identifiers. Up to 64 states may be listed; initialState defaults to the first. The current state and as many as 32 active timers are saved.
behavior = {
states = { "idle", "opening", "open" },
initialState = "idle",
onStateExit = function(ctx)
-- ctx.previous is the state being left; ctx.state is the destination.
end,
onStateEnter = function(ctx)
if ctx.state == "open" then
ctx.entity:startTimer("close", 100)
end
end,
onTimer = function(ctx)
if ctx.name == "close" then
ctx.entity:setBehaviorState("idle")
end
end
}getBehaviorState() returns the current state. setBehaviorState(state) calls onStateExit before changing the saved state and onStateEnter afterward; it returns false when the state was already selected. Transition callback failures are isolated and do not leave the state half-changed.
startTimer(name, ticks, repeatTicks?) starts or replaces a timer using 1-120000 simulation ticks. With repeatTicks, BetaMoon reschedules it before calling onTimer; otherwise it removes the timer first. getTimer(name) returns remaining ticks and cancelTimer(name) removes it. Timers advance only while the entity simulates, so chunk unload and simulation suspension pause them. Each behavior callback receives entity, world, and age; timer contexts add name and repeating. A failure disables only that callback for the current declaration.
After a definition reload, a current state that no longer exists resets to the new initialState without transition callbacks. Removing onTimer clears active timers when the instance next simulates; compatible state and timer values otherwise continue.
- Installation
- Install scripts
- Getting started
- VS Code and LuaLS
- Examples
- Reload and compatibility
- Troubleshooting
- Changelog
Scripts and shared values
- Script lifecycle
- Packaged Lua mods
- Modules
- References and stacks
- Queries
- Overrides
- Minecraft constants
- Chat and positions
- Structured data
- Script configuration
- Particles
Blocks and items
- Multiblocks
- Blocks
- Block state and placement
- Block shapes and rendering
- Block callbacks
- Block ticks and redstone
- Items
- Fluids
- Item callbacks
- Tools and armor
- Materials
- Textures
Entities
- Entities
- Entity access and movement
- Entity lifecycle and data
- Entity health and loot
- Entity inventories and relationships
- Projectiles and pickups
- Entity presentation and sounds
- Living entities and AI
- Entity parts and sensors
Assets and audio
- Assets
- Models and animations
- Built-in Minecraft models
- Model appearances
- Blockbench workflow
- Audio
- Texture-pack overrides
Events and world actions
World generation
World systems
Inventories and screens
Recipes