Scripts Lua: API de elementos decorativos
Esta página cubre todas las API disponibles para los scripts de elementos decorativos de FreeMinecraftModels: context.prop, context.event, context.player, context.projectile, context.world, context.zones, context.scheduler, context.state, context.cooldowns y context.log. Si estás empezando con los scripts, lee primero Primeros pasos.
context.prop
La tabla proporciona información sobre el elemento decorativo y métodos para controlar sus animaciones. FMM conserva una tabla por elemento para mejorar el rendimiento. Campos como current_location consultan el estado actual al leerse.
Campos
| Campo | Tipo | Notas |
|---|---|---|
prop.model_id | string | El nombre del modelo blueprint (p. ej. "torch_01") |
prop.current_location | tabla de ubicación | La posición actual del elemento. Se consulta al leer el campo; no es una instantánea fija. |
La tabla de ubicación tiene los campos estándar: x, y, z, world, yaw, pitch.
Ejemplo: leyendo la info del prop
return {
api_version = 1,
on_spawn = function(context)
context.log:info("Prop spawned: " .. (context.prop.model_id or "unknown"))
local loc = context.prop.current_location
if loc then
context.log:info("Location: " .. loc.x .. ", " .. loc.y .. ", " .. loc.z)
end
end
}
prop:play_animation(name, blend, loop)
Reproduce una animación nombrada en el modelo del prop.
| Parámetro | Tipo | Predeterminado | Notas |
|---|---|---|---|
name | string | requerido | El nombre de la animación como se define en el archivo del modelo |
blend | booleano | true | Con true, pone el cambio en cola tras el tick del estado actual; con false, interrumpe inmediatamente. No mezcla las animaciones. |
loop | booleano | true | Si una animación personalizada se repite; los estados integrados usan su propia política |
Devuelve true cuando se acepta la solicitud, incluso si queda en cola o se omite la reproducción; devuelve false cuando no puede aceptarse. El estado terminal death rechaza reproducciones posteriores. Una solicitud de ataque durante una animación personalizada se acepta sin interrumpirla. La cola solo tiene una posición: una solicitud posterior sustituye a la anterior. La aceptación no demuestra que se haya mostrado ningún fotograma; consulta Animaciones.
Ejemplo
return {
api_version = 1,
on_right_click = function(context)
local success = context.prop:play_animation("open", true, false)
if not success then
context.log:warn("Animation 'open' not found on this model!")
end
end
}
prop:stop_animation()
Borra la reproducción en cola y vuelve a idle si está disponible; en caso contrario, detiene el estado compartido actual. El estado terminal death permanece activo en el motor compartido, aunque la llamada también envía una solicitud de parada al sistema Bedrock.
No toma parámetros.
Ejemplo
return {
api_version = 1,
on_right_click = function(context)
context.prop:stop_animation()
end
}
prop:hurt_visual()
Reproduce la animación visual de daño (destello rojo) en el prop sin causarle ningún daño real.
No toma parámetros.
Ejemplo
return {
api_version = 1,
on_left_click = function(context)
-- Flash red when punched, but don't actually take damage
if context.event then
context.event.cancel()
end
context.prop:hurt_visual()
end
}
prop:pickup()
Elimina el prop del mundo y suelta un objeto de papel de colocación en su ubicación. Se puede hacer clic derecho con el objeto soltado sobre un bloque para volver a colocar el prop.
No recibe parámetros ni devuelve un valor. La eliminación y la caída del objeto se ponen en cola en el hilo principal de Bukkit. No comprueba permisos ni conserva el inventario o libro almacenados en el objeto de colocación. Decide el acceso en el evento que lo llama y gestiona el contenido almacenado antes de recoger el elemento. No pongas en cola varias recogidas del mismo elemento.
Ejemplo
return {
api_version = 1,
on_right_click = function(context)
-- Let players pick up the prop by right-clicking it
context.prop:pickup()
end
}
prop:mount(player)
Monta a un jugador en el primer asiento de mount point disponible en el prop. El modelo debe tener huesos de punto de montaje definidos.
| Parámetro | Tipo | Notas |
|---|---|---|
player | tabla de entidad | Una tabla de entidad de jugador (p. ej. desde context.player o context.event.player) |
Devuelve true cuando se encontró al jugador, el prop tiene puntos de montaje y la acción de montar quedó encolada. Devuelve false cuando esas referencias básicas no son válidas. Un retorno true no prueba que hubiera finalmente un asiento disponible cuando se ejecutó la acción encolada.
Si el script gestiona los asientos al hacer clic derecho, cancela context.event para que el comportamiento predeterminado de FMM no intente sentar al jugador también.
Ejemplo
return {
api_version = 1,
on_right_click = function(context)
if context.event then context.event.cancel() end
local player = context.event and context.event.player
if player then
context.prop:mount(player)
end
end
}
prop:dismount(player)
Desmonta a un jugador de su asiento de mount point en el prop.
| Parámetro | Tipo | Notas |
|---|---|---|
player | tabla de entidad | Una tabla de entidad de jugador |
Devuelve true cuando se encontró al jugador, el prop tiene un gestor de montaje y la comprobación de desmontaje quedó encolada. Devuelve false cuando esas referencias básicas no son válidas.
Ejemplo
return {
api_version = 1,
on_right_click = function(context)
if context.event then context.event.cancel() end
local player = context.event and context.event.player
if player then
-- Toggle mount/dismount
local passengers = context.prop:get_passengers()
for i = 1, #passengers do
if passengers[i].uuid == player.uuid then
context.prop:dismount(player)
return
end
end
context.prop:mount(player)
end
end
}
prop:get_passengers()
Devuelve un array Lua de tablas de entidad para todos los pasajeros actuales en el prop.
No toma parámetros.
Ejemplo
return {
api_version = 1,
on_game_tick = function(context)
local passengers = context.prop:get_passengers()
if #passengers > 0 then
context.log:info("Prop has " .. #passengers .. " passenger(s)")
end
end
}
prop:has_mount_points()
Devuelve si este prop tiene huesos de punto de montaje definidos en su modelo.
No toma parámetros. Devuelve true o false.
Ejemplo
return {
api_version = 1,
on_right_click = function(context)
if context.event then context.event.cancel() end
local player = context.event and context.event.player
if player and context.prop:has_mount_points() then
context.prop:mount(player)
end
end
}
prop:spawn_elitemobs_boss(filename, x, y, z)
Genera un jefe personalizado de EliteMobs en la ubicación dada. Requiere que EliteMobs esté instalado en el servidor.
| Parámetro | Tipo | Notas |
|---|---|---|
filename | string | El nombre de archivo del jefe personalizado (p. ej. "my_boss.yml") |
x | número | Coordenada X |
y | número | Coordenada Y |
z | número | Coordenada Z |
Devuelve una tabla de entidad viva del jefe generado, o nil cuando la integración no puede devolver un jefe vivo, por ejemplo si el plugin no está disponible, falta la configuración o falla la generación. nil no identifica la causa.
Ejemplo
return {
api_version = 1,
on_right_click = function(context)
local loc = context.prop.current_location
if loc then
local boss = context.prop:spawn_elitemobs_boss("dungeon_guardian.yml", loc.x, loc.y + 1, loc.z)
if boss then
context.log:info("Spawned boss: " .. (boss.name or "unknown"))
else
context.log:warn("Could not spawn boss -- is EliteMobs installed?")
end
end
end
}
prop:open_inventory(player, title, rows)
Pone en cola la apertura de un inventario de cofre. Cada elemento tiene un único inventario compartido: la primera apertura lo crea a partir del contenido guardado en el PersistentDataContainer del elemento, y las aperturas posteriores muestran ese mismo inventario en tiempo real a todos los usuarios. FMM lo vuelve a guardar en el elemento cuando lo cierra el último usuario, cuando se descarga el chunk del elemento y antes de que drop_inventory() lo lea.
| Parámetro | Tipo | Predeterminado | Notas |
|---|---|---|---|
player | tabla de entidad | requerido | El jugador al que mostrar el inventario |
title | string | requerido | El título del inventario (soporta códigos de color &) |
rows | int | 3 | Número de filas (1-6, donde 6 = 54 ranuras = doble cofre) |
Devuelve true si las referencias al elemento y al jugador eran válidas y se puso en cola la apertura; en caso contrario, devuelve false. Las filas se limitan al intervalo de 1 a 6 antes de crear el inventario.
El tamaño y el título proceden de la llamada que crea el inventario; mientras está abierto, las llamadas posteriores lo reutilizan tal cual. Cuando el contenido guardado ocupa más ranuras que las filas solicitadas, el inventario crece para que quepan, así que un valor de rows menor nunca descarta objetos almacenados. Si no se pueden leer los datos guardados, el inventario no se abre. La acción se omite cuando el elemento se eliminó antes de que se ejecutara. El resultado no garantiza que el inventario se haya abierto.
prop:is_viewing_inventory(player)
Devuelve si el listener de inventario tiene registrado al jugador como usuario del inventario de este elemento.
| Parámetro | Tipo | Notas |
|---|---|---|
player | tabla de entidad | El jugador a comprobar |
Devuelve true o false.
Ejemplo: Animación de cierre cuando se cierra el inventario
context.state["task_" .. player.uuid] = context.scheduler:run_repeating(5, 5, function(tick_context)
if not tick_context.prop:is_viewing_inventory(player) then
tick_context.prop:play_animation("close", true, false)
tick_context.scheduler:cancel(tick_context.state["task_" .. player.uuid])
end
end)
prop:place_book(player)
Toma el libro escrito o escribible de la mano principal del jugador y lo almacena en el prop.
| Parámetro | Tipo | Notas |
|---|---|---|
player | tabla de entidad | El jugador que sostiene el libro |
Devuelve true si las referencias al elemento y al jugador eran válidas y la acción se puso en cola. La acción posterior del hilo principal solo coloca un libro si el jugador sostiene un libro editable o escrito y el elemento aún no tiene uno.
prop:read_book(player)
Abre el libro almacenado para que el jugador lo lea.
| Parámetro | Tipo | Notas |
|---|---|---|
player | tabla de entidad | El jugador al que mostrar el libro |
Devuelve true si las referencias al elemento y al jugador eran válidas y se puso en cola la lectura. La acción posterior requiere un WRITTEN_BOOK almacenado que pueda deserializarse. Este método no abre para lectura un libro editable almacenado.
prop:take_book(player)
Devuelve el libro almacenado al inventario del jugador y lo elimina del prop.
| Parámetro | Tipo | Notas |
|---|---|---|
player | tabla de entidad | El jugador al que dar el libro |
Devuelve true si las referencias al elemento y al jugador eran válidas y la retirada se puso en cola. No garantiza que hubiera un libro almacenado.
Deja espacio en el inventario: la entrada almacenada se elimina antes de añadir el libro y el exceso no se suelta al suelo. place_book serializa la pila sostenida antes de reducir su cantidad en uno; usa libros individuales normales.
prop:has_book()
Comprueba si el soporte para armaduras tiene la clave PDC del libro almacenado. No valida el libro serializado ni recibe parámetros.
prop:drop_inventory()
Pone en cola el guardado del inventario abierto, si lo hay, y su cierre para todos los usuarios; después lee el contenido guardado, borra su entrada PDC y suelta los objetos junto al elemento. Se incluyen los cambios hechos en el inventario abierto hasta ese momento. Si falla la decodificación, puede no quedar nada que soltar después de borrar la entrada.
No recibe parámetros. Devuelve true si el elemento tiene un soporte para armaduras válido y la acción de soltar se puso en cola. No garantiza que hubiera objetos almacenados.
prop:drop_book()
Pone en cola la caída del libro almacenado en la ubicación del elemento. La acción borra la entrada antes de decodificarla, por lo que un fallo de decodificación puede dejar la operación sin libro que soltar.
No recibe parámetros. Devuelve true si el elemento tiene un soporte para armaduras válido y la acción de soltar se puso en cola. No garantiza que hubiera un libro almacenado.
prop:set_persistent_data(key, value)
Guarda una cadena en el PersistentDataContainer del soporte para armaduras del elemento. Persiste con ese soporte durante los guardados normales del mundo y las descargas de chunks. Eliminar definitivamente el elemento o sustituirlo por un objeto de colocación no transfiere estos datos.
| Parámetro | Tipo | Notas |
|---|---|---|
key | string | Sufijo único de clave Bukkit con letras minúsculas, dígitos, ., _, - o /; se guarda internamente como fmm_lua_<key>. Las claves no válidas generan un error |
value | string | El valor a almacenar. Usa tostring() para números y booleanos. |
Devuelve true si tiene éxito, false si el prop no tiene un armor stand de respaldo.
prop:get_persistent_data(key)
Recupera un valor string previamente almacenado con set_persistent_data. Devuelve nil si la clave no se ha establecido.
| Parámetro | Tipo | Notas |
|---|---|---|
key | string | El nombre de clave usado en set_persistent_data |
Ejemplo: Estado de toggle persistente
return {
api_version = 1,
on_spawn = function(context)
local saved = context.prop:get_persistent_data("active")
context.state.active = saved == "true"
end,
on_right_click = function(context)
context.state.active = not context.state.active
context.prop:set_persistent_data("active", tostring(context.state.active))
end
}
Efectos de objetos
La API context.item de objetos equipados se ha retirado. Define los efectos mediante encantamientos personalizados y adjúntalos a través de la lista enchantments: del objeto con espacio de nombres.
context.event
Datos del evento actual. Disponible en clics de elementos decorativos, impactos de proyectiles y eventos genéricos de zona. Devuelve nil en los eventos sin evento asociado ni jugador actor (on_spawn, on_game_tick, on_destroy).
Los clics de FMM usan una marca de cancelación del plugin para su comportamiento predeterminado. La interfaz Lua solo expone los campos y métodos siguientes.
Campos y métodos
| Campo o Método | Tipo | Notas |
|---|---|---|
event.player | tabla de entidad de jugador | El jugador que activó el evento o cruzó el límite de una zona genérica vigilada. Disponible en on_left_click, on_right_click, on_zone_enter y on_zone_leave de elementos decorativos. En on_projectile_hit es la entidad viva que disparó el proyectil, que puede ser un mob, y no está presente cuando quien disparó no es una entidad viva. Consulta Métodos de entidad de jugador para todos los campos y métodos. |
event.is_cancelled | booleano | Estado de cancelación en el momento en que se construyó el context. Este campo no se refresca tras llamar a cancel() o uncancel(). |
event.cancel() | función | Cancela el evento (p. ej. previene daño o interacción) |
event.uncancel() | función | Descancela un evento previamente cancelado |
cancel y uncancel ignoran los argumentos, así que context.event.cancel() y context.event:cancel() se comportan igual. Esta página usa la forma con punto; ambas funcionan.
Los eventos genéricos de zona proporcionan un jugador actor sin marca de cancelación, por lo que event.cancel() y event.uncancel() no existen y event.is_cancelled es false. Los clics de elementos decorativos proporcionan la marca de cancelación de FMM; cancelarla impide la acción predeterminada correspondiente.
Trata event.is_cancelled como una instantánea del estado inicial. Si tu propio script llama a event.cancel() o event.uncancel(), guarda tu propia marca local si necesitas recordar ese cambio más adelante en el mismo hook.
La tabla de evento actual de FMM no expone campos específicos de Bukkit como target, block, projectile o item. Usa context.player, context.event.player, context.projectile (en on_projectile_hit), player:get_target_entity(range), context.world:raycast(...) o consultas de entidades cercanas cuando necesites contexto adicional.
on_projectile_hit
Se activa cuando un proyectil impacta en la hitbox del elemento, una vez por proyectil.
| Valor del contexto | Notas |
|---|---|
context.projectile | Tabla de entidad del proyectil (flecha, tridente, bola de nieve, etc.). Solo se define en este evento |
context.event.player | La entidad viva que disparó el proyectil, si la hay |
context.player | Quien disparó, si es un jugador; en caso contrario, nil |
El evento se ejecuta después de que FMM haya gestionado el impacto, y la gestión predeterminada de proyectiles de FMM no hace nada con los elementos decorativos. Llamar aquí a context.event.cancel() no impide que el impacto consuma un proyectil que no atraviesa. El evento no se activa cuando otro plugin ya canceló el impacto.
return {
api_version = 1,
on_projectile_hit = function(context)
local shooter = context.player
if shooter then
shooter:send_message("&eYou hit the target!")
end
context.prop:play_animation("hit", false, false)
end
}
Ejemplo: cancelar el daño del clic izquierdo
Ejemplo
return {
api_version = 1,
on_left_click = function(context)
if context.event then
context.event.cancel()
end
end
}
Ejemplo: comprobar el estado de cancelación
Ejemplo
return {
api_version = 1,
on_left_click = function(context)
if context.event and not context.event.is_cancelled then
context.event.cancel()
context.log:info("Damage cancelled!")
end
end
}
Dentro de callbacks programados (scheduler:run_later, scheduler:run_repeating), context.event siempre es nil. La modificación del evento solo puede ocurrir durante el propio hook del evento.
context.world
Esta es la API de world de FreeMinecraftModels/MagmaCore. Consulta context.world para la referencia completa.
Todos los métodos documentados en la página global (get_block_at, set_block_at, spawn_particle, play_sound, strike_lightning, get_time, set_time, get_nearby_entities, get_nearby_players, spawn_entity, get_highest_block_y, raycast, place_temporary_block, drop_item, spawn_firework) están disponibles en FMM. Consulta la API de mundo de MagmaCore para los detalles de world:raycast() (trazar un rayo y detectar entidades o bloques alcanzados), world:place_temporary_block() (reemplazo temporal de bloques) y world:spawn_firework() (generar cohetes de fuegos artificiales con colores y formas personalizados). Los poderes de jefes de EliteMobs parten de la misma base y añaden métodos específicos con tablas de ubicación para generar jefes, refuerzos, bloques que caen, bloques temporales y más; consulta Mundo y entorno de EliteMobs.
Funciones de mundo específicas de FMM
FreeMinecraftModels superpone tres ayudantes opcionales de botín de EliteMobs sobre context.world. Siempre están presentes, pero cada uno devuelve false y no hace nada cuando EliteMobs no está instalado:
| Método | Notas |
|---|---|
world:drop_elitemobs_procedural_loot(player, level, location?) | Genera un objeto de EliteMobs para el jugador. Respeta la configuración de entrega directa al inventario; el exceso cae junto al jugador. En los demás casos usa la ubicación elegida. Devuelve false si los objetos procedurales están desactivados |
world:drop_elitemobs_random_loot(player, level, location?) | Tira las tablas de botín de EliteMobs para el jugador en el nivel indicado |
world:drop_elitemobs_custom_loot(player, file, level, location?) | Suelta para el jugador un archivo concreto de objeto personalizado de EliteMobs. Devuelve false cuando el archivo no se resuelve |
Las firmas completas están en la Referencia de la API de Lua. El equivalente del lado del prop para los jefes es prop:spawn_elitemobs_boss(...), documentado más arriba.
Métodos de entidad de jugador
Las tablas de entidad de jugador se devuelven desde context.player, context.event.player y context.world:get_nearby_players(). Los hooks genéricos de MagmaCore on_zone_enter / on_zone_leave establecen context.player y context.event.player al jugador que entra o sale.
Las tablas de entidad, los métodos de entidades vivas, los métodos específicos de jugador y los métodos de interfaz de jugador documentados en la página global son las tablas de MagmaCore usadas por FMM. Los poderes de jefes de EliteMobs exponen tablas parecidas pero específicas, documentadas en Jefes y entidades. Consulta el Motor de scripts Lua de MagmaCore para la referencia completa de FMM: campos base de entidades, campos y métodos de entidades vivas, campos y métodos específicos de jugador y Métodos de interfaz de jugador. Los métodos nuevos incluyen player:get_target_entity() (selección por trazado de rayos), player:get_eye_location(), player:get_look_direction(), player:send_block_change() (bloques falsos por jugador) y player:reset_block(); consulta Métodos específicos de jugador.
Campos de entidad específicos de FMM
Cada tabla de entidad construida dentro de un script de FMM obtiene estos campos extra automáticamente (a través del LuaEntityEnricher de FMM):
| Campo | Tipo | Notas |
|---|---|---|
entity.is_modeled | booleano | true si esta entidad Bukkit es la entidad subyacente de un ModeledEntity |
entity.is_prop | booleano | true si esta entidad es un armor stand que respalda un PropEntity |
entity.model | tabla o nil | Poblado solo cuando is_modeled = true (ver abajo) |
Estas marcas de FMM y la referencia del modelo se capturan al construir la tabla; no se actualizan en cada lectura. Obtén una tabla nueva si cambia el modelo adjunto. Las tablas de jugador también ofrecen has_permission(node) y una instantánea is_op; has_permission comprueba los permisos actuales.
Cuando entity.model está presente, expone:
| Campo / Método | Notas |
|---|---|
model.model_id | El nombre del modelo blueprint (p. ej. "dragon") |
model.is_dynamic | true si es un DynamicEntity (adjunto a una entidad viva) |
model:play_animation(name, blend, loop) | Solicita una animación por su nombre. blend y loop valen false por defecto en este puente. true indica aceptación, no necesariamente reproducción visible |
model:stop_animations() | Borra la reproducción en cola y vuelve a reposo si está disponible; no interrumpe el estado terminal de muerte |
model:remove() | Elimina inmediatamente la entidad modelada y todos sus huesos |
on_right_click = function(context)
local player = context.event and context.event.player
if not player then return end
local target = player:get_target_entity(8)
if target and target.is_modeled then
target.model:play_animation("hurt", true, false)
end
end
Campos de entidad de EliteMobs
Cuando EliteMobs está instalado, FMM reenvía al enricher de EliteMobs para que las mismas tablas de entidad también expongan:
| Campo | Tipo | Notas |
|---|---|---|
entity.is_elite | booleano | true si la entidad es rastreada por EliteMobs |
entity.is_custom_boss | booleano | true si es una configuración de jefe personalizado |
entity.is_significant_boss | booleano | true para jefes personalizados con healthMultiplier > 1 (filtra mobs basura con nombre) |
entity.elite | tabla o nil | Poblado solo cuando is_elite = true. Contiene level, name, health, max_health, health_multiplier, damage_multiplier, is_custom_boss, además de elite:remove() |
Las marcas y la mayoría de los campos de élite son instantáneas; health y max_health se consultan al leerlos. Con EliteMobs activado, entity:can_receive_hostile_effect(player_uuid) también aplica la autorización de objetivos mágicos de EliteMobs después de la comprobación compartida de objetivos hostiles. Si el adaptador no está disponible, deniega la comprobación en lugar de tratar a los aliados como objetivos sin integración.
context.zones
Esta es la API de zones de FreeMinecraftModels/MagmaCore. Consulta context.zones para la referencia completa. Los poderes de jefe de EliteMobs usan una tabla context.zones distinta con definiciones de zona nativas de EliteMobs; consulta EliteMobs Zones & Targeting.
context.scheduler
La API de scheduler documentada aquí es el planificador de MagmaCore usado por los scripts de FreeMinecraftModels y los scripts de NPC de EliteMobs. Los nombres al estilo de EliteMobs (run_after, run_every y cancel_task) son alias sobre el planificador compartido, así que cualquiera de los dos estilos funciona. Los poderes de jefe exponen los mismos alias a través de su context específico de jefe. Consulta context.scheduler para la referencia completa de FMM.
context.state
La API de state es compartida por los scripts de FreeMinecraftModels, los poderes de jefe de EliteMobs y los scripts de NPC de EliteMobs. Consulta context.state para la referencia completa.
context.log
La API de logging documentada aquí es el logger de FreeMinecraftModels/MagmaCore (info, warn, error). Los scripts de NPC de EliteMobs usan el mismo logger; los poderes de jefe de EliteMobs exponen info, warn y debug. Consulta context.log para la referencia completa.
context.cooldowns
La API de cooldowns documentada aquí es el orden compartido de MagmaCore/FMM usado por los scripts de FreeMinecraftModels y los scripts de NPC de EliteMobs: check_local(key?, duration). Los poderes de jefe de EliteMobs usan el mismo orden de argumentos con almacenes de respaldo específicos de jefe. Consulta context.cooldowns para la referencia completa.
| Método | Notas |
|---|---|
local_ready(key?) | Comprueba si un cooldown local está listo. |
local_remaining(key?) | Devuelve los ticks restantes del cooldown local, o 0. |
check_local(key?, duration) | Comprueba e inicia un cooldown local de forma atómica. |
set_local(duration, key?) | Establece un cooldown local sin comprobarlo. |
global_ready() | Comprueba el cooldown global compartido del propietario del script. |
set_global(duration) | Establece el cooldown global compartido del propietario del script. |
Usa context.cooldowns:check_local("my_key", 40) para los tiempos de reutilización normales de acciones de elementos decorativos.
Modelo de ejecución
Un entorno de ejecución por instancia de script
Cada entrada de la lista scripts: de un elemento obtiene un entorno Lua independiente. FMM descubre y valida el código al registrar el proveedor; después evalúa el código almacenado en caché en un entorno aislado nuevo para cada vinculación. Editar o añadir un archivo Lua requiere una recarga correcta antes de que las nuevas vinculaciones lo usen. Los scripts se ejecutan por orden de la lista configurada; priority se lee pero no altera ese orden. Al eliminar el elemento se cierran sus instancias de script.
Los encantamientos de objetos tienen un ciclo de vida separado; no crean una instancia de script de elemento decorativo para el objeto equipado.
Esto significa:
- Las variables locales declaradas en el ámbito de archivo son privadas para esa instancia de script.
context.stateestá completamente aislado entre instancias, incluso si comparten el mismo archivo de script.
Propiedad de las tareas programadas
Todas las tareas creadas a través de context.scheduler son propiedad del runtime que las creó. Cuando se elimina un prop:
- El runtime se cierra.
- Cada tarea propia -- tanto de una sola vez como recurrente -- se cancela automáticamente.
- Todas las observaciones de zona se limpian.
on_destroy se ejecuta primero solo cuando el prop se elimina definitivamente: al recogerlo, romperlo, borrarlo con /fmm deleteall o cuando lo elimina un plugin. Cuando se descarga el chunk o el mundo del prop, o FMM se recarga o se apaga, el runtime se detiene sin llamar a on_destroy. El prop vuelve con un runtime nuevo, y un nuevo on_spawn, cuando su chunk se carga de nuevo.
Alcance de los tiempos de reutilización
El motor de scripting compartido expone helpers de cooldown local (local_ready, local_remaining, check_local, set_local) y helpers de cooldown global (global_ready, set_global). FMM define el alcance de esos almacenes de la siguiente forma:
| Tipo de script | Alcance del store local | Alcance del store global |
|---|---|---|
| Script de prop | Por ScriptInstance (prop + archivo de script) | Por PropEntity (compartido entre todos los scripts vinculados a ese prop) |
Los tiempos de reutilización globales se borran al retirar el elemento del registro o al cerrar el gestor de scripts durante una recarga correcta o la desactivación del plugin. Si se rechaza la preparación de una recarga, se conserva el entorno actual. Las duraciones usan tiempo transcurrido monotónico a razón de 50 ms por tick solicitado; el planificador cuenta ticks del servidor, por lo que ambos pueden diferir con lag.
Límites de ejecución
Cada invocación de hook, cada callback programado y la propia evaluación inicial del archivo de script se ejecutan bajo un presupuesto de ejecución estricto. El presupuesto se aplica dentro de la VM de Lua, así que actúa mientras tu código sigue ejecutándose en lugar de limitarse a mirar el reloj después.
| Límite | Valor |
|---|---|
| Tiempo de CPU del hilo actual | 50 milisegundos |
| Instrucciones de Lua ejecutadas | 250,000 |
El límite que se alcance primero aborta la llamada con un error de Lua y deshabilita la instancia del script. Los mensajes son:
Lua instruction budget exceeded (250000 instruction limit)
Lua CPU-time budget exceeded (50ms current-thread CPU limit)
El contador de instrucciones limita un bucle Lua puro como while true do end. El tiempo se comprueba cada 1024 instrucciones Lua y al volver de la llamada. El supervisor no interrumpe un método Java/Bukkit bloqueante mientras se está ejecutando.
La mitad temporal del presupuesto se mide como tiempo de CPU del hilo actual, no como tiempo de reloj, así que a un script no se le cobra el tiempo en el que el hilo del servidor estuvo fuera de planificación. En una JVM donde la medición de tiempo de CPU del hilo actual no esté disponible, MagmaCore recae en un límite deliberadamente más generoso de 250 milisegundos de tiempo transcurrido (Lua elapsed-time fallback budget exceeded (250ms fallback; current-thread CPU time unavailable)) manteniendo el mismo techo de 250.000 instrucciones, de modo que los scripts que no terminan siguen estando acotados en cualquier caso.
Las llamadas anidadas comparten un único presupuesto: si un hook invoca un callback que invoca otro, toda la cadena se mide como una única asignación de 50 ms de CPU / 250.000 instrucciones, no una asignación para cada uno.
La evaluación durante el descubrimiento del proveedor rechaza las definiciones no válidas antes de registrarlas. El código almacenado en caché vuelve a evaluarse para cada instancia nueva, con los mismos límites.
Para mantenerse dentro del presupuesto:
- Evita bucles sin límite dentro de hooks.
- Mantén los manejadores
on_game_tickligeros -- se ejecutan en cada tick. - Usa
context.scheduler:run_repeating(...)para repartir el trabajo entre ticks.
Referencia completa de eventos de script
Esta tabla enumera los eventos activos de elementos decorativos.
La columna context.event describe la marca de cancelación de FMM o el actor de zona. La interfaz Lua expone event.player, event.is_cancelled, event.cancel() y event.uncancel() cuando corresponde.
Eventos activos de elementos decorativos (8)
| Hook | Se dispara cuando | context.event |
|---|---|---|
on_spawn | El prop aparece en el mundo, o vuelve a cargarse con su chunk | nil |
on_game_tick | Cada tick del servidor (50 ms a 20 TPS) | nil |
on_destroy | El prop se elimina definitivamente (no al descargarse el chunk ni al recargar) | nil |
on_left_click | El jugador hace clic izquierdo en el elemento | marca de cancelación del daño predeterminado |
on_right_click | El jugador hace clic derecho en el elemento | marca de cancelación de la interacción predeterminada |
on_zone_enter | El jugador entra en una zona observada | actor jugador de zona (context.player / context.event.player; no cancelable) |
on_zone_leave | El jugador sale de una zona observada | actor jugador de zona (context.player / context.event.player; no cancelable) |
on_projectile_hit | Un proyectil impacta en el prop | evento de impacto de proyectil; event.player es quien disparó. Consulta on_projectile_hit |
Efectos de objetos
Usa encantamientos personalizados para efectos de objetos que se sostienen. La antigua tabla de 22 eventos de scripts de objetos ya no forma parte del contrato de ejecución.
Siguientes pasos
- Ejemplos y patrones -- scripts de elementos decorativos con explicaciones y notas de preparación
- Solución de Problemas -- problemas comunes, consejos de depuración y una checklist de QC
- Primeros Pasos -- estructura de archivos, hooks, recorrido del primer script
Transporte de EliteMobs
context.world:start_elitemobs_transport(player, route_id) inicia una ruta guardada de EliteMobs para un jugador en el mundo del contexto. Devuelve si se aceptó la solicitud y requiere EliteMobs con esa ruta cargada. El ID no lleva extensión .yml. Consulta rutas de transporte.