Scripts Lua: solución de problemas
Esta página cubre los problemas habituales al escribir o depurar scripts de elementos decorativos de FreeMinecraftModels, además de consejos sobre la generación diferida de configuración. Si buscas ejemplos funcionales, consulta Ejemplos y patrones. Si estás empezando, consulta Primeros pasos.
FMM usa el runtime ScriptInstance de MagmaCore. Para la documentación del sandbox, el planificador, las zonas, la API de mundo, las tablas de entidad y los métodos de UI de jugador de FMM/MagmaCore, consulta la página del Motor de Scripting Lua de MagmaCore. Los poderes de jefe y los scripts de NPC de EliteMobs reutilizan el sandbox de Lua pero exponen sus propias tablas de context.
Problemas habituales
1. El archivo de configuración no se carga / El script no está asignado
Síntoma: El prop aparece pero no responde a clics ni a ningún hook.
Causas y soluciones:
-
Aún no existe un archivo
.yml. FMM intenta crearlo de forma asíncrona al generar el primer prop, con valores predeterminados (habilitado, sin scripts), sin sobrescribir archivos existentes. Añade los nombres de scripts, completa/fmm reloadcorrectamente y coloca un prop nuevo. Tanto YAML como Lua requieren recarga tras una edición. -
La configuración tiene
isEnabled: false. Abre el archivo.ymljunto al archivo del modelo y estableceisEnabled: true. -
La lista
scripts:está vacía. Agrega los nombres de tus archivos de script:isEnabled: true
scripts:
- my_script.lua -
El nombre del archivo
.ymlno coincide con el nombre del archivo del modelo. La configuración debe tener el mismo nombre base que el archivo del modelo. Por ejemplo,torch_01.fmmodelnecesitatorch_01.ymlen el mismo directorio.
2. Archivo de script no encontrado
Síntoma: La consola muestra: [FMM Scripts] Script 'my_script.lua' not found in scripts/ folder
Causas y soluciones:
-
Directorio incorrecto. Los archivos de script deben estar en
plugins/FreeMinecraftModels/scripts/, no junto al archivo del modelo. -
El nombre de archivo no coincide. El nombre de la configuración
.ymldebe coincidir con el archivo descripts/, incluidas las mayúsculas y minúsculas. Se recomienda incluir.luaen la configuración, pero es opcional: FMM añade esa extensión internamente si la omites. -
Archivo no presente. Verifica que el archivo
.luarealmente exista en la carpetascripts/. -
Definición en caché o nombres repetidos. FMM busca scripts recursivamente al inicializar el proveedor, pero los vincula por nombre de archivo, no por ruta relativa. Usa nombres únicos entre subcarpetas. Después de añadir o modificar un archivo Lua, ejecuta
/fmm reloady comprueba que termina correctamente; volver a generar el prop no vuelve a leer el código Lua almacenado en caché.
3. El script no se carga en absoluto
Síntoma: No hay error en la consola, pero ningún hook se activa.
Revisa la consola del servidor en busca de errores de sintaxis Lua durante el inicio. Las causas más comunes son:
- Falta
endpara cerrar una función o un bloqueif - Paréntesis no emparejados
- Falta coma entre definiciones de hooks en la tabla retornada
- El archivo no termina con
.lua
Los errores de descubrimiento aparecen como avisos de scripts inválidos o retirados, o como errores de carga. Los fallos de eventos y funciones programadas usan un bloque [Lua] con el archivo y el contexto de ejecución; el número de línea solo aparece si se puede extraer de la excepción.
4. El evento de script nunca se activa
Síntoma: Aparece tu propio mensaje de inicio, pero un evento concreto nunca se activa.
Verifica que el nombre esté escrito exactamente como aparece en la referencia de eventos de script. Errores habituales:
| Nombre incorrecto | Nombre correcto |
|---|---|
on_click | on_right_click o on_left_click |
on_interact | on_right_click |
on_hit | on_left_click |
on_tick | on_game_tick |
on_remove | on_destroy (solo se ejecuta cuando el prop se elimina definitivamente, no al descargarse el chunk ni al recargar) |
on_arrow_hit | on_projectile_hit |
Comprueba que el modelo se cargó, tiene una hitbox utilizable y ningún listener de protección canceló la interacción antes de la función del prop. Un prop generado usa un soporte para armaduras; cambiar una opción de configuración no permite hacer clic en un modelo inválido o sin cargar.
5. Tiempo de espera agotado / Límite de ejecución superado
Síntoma: La consola muestra un error Lua generado dentro de la VM. Una llamada que supera el presupuesto se interrumpe durante la ejecución con uno de estos mensajes:
Lua instruction budget exceeded (250000 instruction limit)
Lua CPU-time budget exceeded (50ms current-thread CPU limit)
Lua elapsed-time fallback budget exceeded (250ms fallback; current-thread CPU time unavailable)
Cada hook, callback y evaluación de archivo puede ejecutar como máximo 250.000 instrucciones Lua o consumir 50 ms de CPU del hilo actual, lo que se alcance primero. Las llamadas anidadas comparten el mismo presupuesto. Si la JVM no permite medir la CPU por hilo, se usa un límite alternativo de 250 ms de tiempo transcurrido; solo entonces aparece el tercer mensaje. El script realiza demasiado trabajo en una sola llamada. Causas comunes:
- Iterar sobre demasiadas entidades en
on_game_tick - Crear demasiadas partículas por tick
- Ejecutar bucles costosos sin repartir el trabajo entre varios ticks
Solución: Limita el trabajo costoso con context.cooldowns o usa scheduler:run_repeating() con un intervalo razonable en lugar de on_game_tick.
El límite de instrucciones acota los bucles Lua. El tiempo se comprueba cada 1024 instrucciones Lua y al volver; no se interrumpe una llamada Java/Bukkit bloqueante a mitad de ejecución. Limita también el trabajo dentro de cada función programada.
6. context.event es nil en un evento de clic
Esto normalmente no debería ocurrir para on_left_click y on_right_click, pero siempre protégete con:
if context.event then
context.event.cancel()
end
Dentro de los callbacks del scheduler, context.event siempre es nil -- este es el comportamiento esperado. La modificación de eventos solo puede ocurrir dentro del hook del evento mismo.
7. La animación no se reproduce
Síntoma: play_animation() devuelve false, o no ocurre nada visible.
Causas y soluciones:
-
Nombre de animación incorrecto. El nombre debe coincidir exactamente con lo definido en el archivo del modelo. Revisa tu archivo
.bbmodelo.fmmodelpara obtener el nombre correcto de la animación. -
El modelo no tiene animaciones. No todos los modelos tienen animaciones. Verifica que el archivo del modelo realmente contenga datos de animación.
-
El espectador no tiene el paquete o está fuera del alcance de visualización del modelo. Los jugadores necesitan tanto el paquete de recursos actual como un modelo visible.
-
La petición está en cola o se ha suprimido. Pasar
trueenblendusa una única posición en cola, no una transición gradual. Una petición posterior puede sustituirla y un ataque durante una animación personalizada puede devolvertruesin interrumpirla. Usa los argumentos posicionales de Animaciones y distingue una petición aceptada de un fotograma mostrado.
8. Las partículas no aparecen
- Los nombres de partículas aceptan
FLAMEyflame; la API normaliza las mayúsculas antes de buscar la partícula de Bukkit. - Verifica que la ubicación esté en un chunk cargado. Si no hay jugadores cerca, el chunk puede estar descargado.
- Verifica que
countsea al menos 1. - Algunas partículas (como
DUST) necesitan datos adicionales específicos que elspawn_particle()básico puede no soportar. Usa partículas estándar comoFLAME,HEART,HAPPY_VILLAGER,NOTE,ENCHANT, etc.
9. El sonido no se reproduce
- Los sonidos aceptan nombres enum de Bukkit sin distinguir mayúsculas o claves válidas como
minecraft:block.note_block.harpy claves de packs personalizados. El cliente debe disponer del sonido en su pack de recursos. - Verifica que las coordenadas de ubicación sean correctas (no todas cero ni NaN).
- Verifica que el volumen sea mayor que 0.
10. El estado se reinicia inesperadamente
context.state pertenece a una instancia de script. Las entradas separadas de scripts: del mismo prop no lo comparten. Se conserva mientras la instancia está activa. Si parece reiniciarse:
- El prop puede haber sido eliminado y vuelto a generar, o su chunk puede haberse descargado y vuelto a cargar. Cada spawn o carga del chunk crea una nueva instancia. Guarda con
prop:set_persistent_datalos valores que deban sobrevivir a esto. - Una recarga correcta o un error de script puede haber cerrado la instancia anterior. Revisa la consola; corregir el archivo no reactiva una instancia cerrada sin recargarlo y volver a vincularlo.
- Puede que estés leyendo el estado en un callback del scheduler usando la variable de contexto incorrecta. Usa siempre el parámetro de contexto propio del callback.
11. La biblioteca os no está disponible
Síntoma: El script se cae con un error como attempt to index nil (os).
La librería os está completamente eliminada del sandbox de Lua. No puedes usar os.time(), os.clock() ni ninguna otra función de os.
Solución: Usa context.world:get_time() para el tiempo del mundo, o lleva la cuenta del tiempo transcurrido manualmente con marcas de estado y contadores de ticks en on_game_tick. Para los tiempos de reutilización, prefiere context.cooldowns:check_local(key, ticks).
12. El script de objeto no se activa
El formato de scripts de objetos equipados se ha retirado. Los objetos personalizados con una lista scripts: no vacía o encantamientos antiguos sin espacio de nombres se omiten y se registran en la consola. Sustitúyelos por una definición actual de objeto personalizado y encantamientos con espacio de nombres. Los scripts de elementos decorativos siguen usando scripts:.
13. La definición del objeto no está disponible
Consulta el aviso de inicio o recarga para conocer el archivo exacto. Un material no disponible, como un tipo de objeto de Minecraft reciente en un servidor antiguo, también provoca que se omita el objeto. Corrige o reemplaza la definición para la versión de tu servidor; FMM no la reescribe ni inventa un material sustituto.
14. El evento se ejecuta, pero el script falla al llamar a métodos de entidad
Síntoma: Un evento como on_right_click funciona, pero el script falla dentro de una función programada con errores como attempt to call nil al llamar a entity:damage() o entity:push().
Causa: context.world:get_nearby_entities() devuelve TODAS las entidades dentro del alcance, incluidas las no vivas: objetos soltados, orbes de experiencia y nubes de efecto de área. Estas entidades no tienen métodos de entidades vivas como damage(), push() o add_potion_effect().
Solución: Protege siempre con if entity.damage then antes de llamar a métodos de entidad viva:
local entities = context.world:get_nearby_entities(x, y, z, radius)
for _, entity in ipairs(entities) do
if entity.damage then
-- Safe to call living entity methods
entity:damage(5.0)
entity:push(0, 0.5, 0)
end
end
15. Faltan datos exclusivos del evento en las funciones programadas
Síntoma: Los datos del evento de clic/combate faltan dentro de un callback de scheduler:run_later() o scheduler:run_repeating().
Causa: Las funciones programadas reciben un contexto nuevo y no conservan el evento original de Bukkit ni el actor genérico de zona. context.event siempre es nil en ellas. Los scripts de elementos decorativos solo reciben context.player de nivel superior durante eventos provocados por un jugador, como clics y entradas o salidas de zona. Deben capturar al jugador desde context.player o context.event.player durante el evento original si lo necesitarán después.
Solución: Usa el contexto nuevo de la función programada para acceder normalmente al mundo y al estado. Si necesitas al jugador o la entidad exactos del evento original, captúralos antes de programar la función y comprueba que siguen siendo válidos.
on_right_click = function(context)
local clicked_player = context.player or (context.event and context.event.player)
context.scheduler:run_later(20, function(later_context)
-- Prop scripts should use a captured player when they need the clicker.
local player = later_context.player or clicked_player
if player and player.is_valid then
player:send_message("&aDelayed message!")
end
end)
end
Cómo funciona la generación diferida de configuración
Entender el sistema de configuración diferida ayuda a evitar confusión al configurar nuevos props:
-
Primer spawn: Cuando un prop aparece y no existe un archivo
.ymlasociado, FMM crea la configuración de forma asíncrona. Esto significa que el archivo se escribe en un hilo de fondo y no está disponible inmediatamente. -
Valores predeterminados: La configuración generada tiene
isEnabled: truey una lista vacíascripts: []. -
Sin scripts en el primer spawn: Como la configuración se crea después de que el prop ya ha aparecido (y no tiene scripts listados), el prop no tendrá scripts asignados en su primer spawn.
-
Editar, recargar y volver a generar: Añade los nombres de scripts y completa
/fmm reloadcorrectamente. Los ajustes, las comprobaciones de colocación y las listas de scripts del menú usan el mismo catálogo preparado. Un prop nuevo usa ese catálogo; no relee el YAML editado. Los cambios de Lua también requieren recarga. -
Ubicación de la configuración: El archivo
.ymlse crea en el mismo directorio que el archivo del modelo, con el mismo nombre base. Por ejemplo:- Modelo:
plugins/FreeMinecraftModels/models/fountain.fmmodel - Configuración:
plugins/FreeMinecraftModels/models/fountain.yml
- Modelo:
- Coloca el modelo en
models/ - Coloca el script en
scripts/ - Genera el prop una vez (genera el
.yml) - Edita el
.ymlpara agregarscripts: [my_script.lua] - Recarga para descubrir el archivo Lua, coloca un prop nuevo y comprueba su propio mensaje de inicio
Lectura de mensajes de error
Los errores de eventos y funciones programadas generan un bloque [Lua] con el archivo y el contexto de ejecución. El número de línea se incluye si la excepción contiene una ubicación reconocible. Los errores de descubrimiento y configuración pueden usar otros avisos.
Un error típico se ve así:
[Lua] Error in 'my_door.lua' at line 12 during 'on_right_click':
[Lua] -> You tried to call a method or function that doesn't exist.
[Lua] -> Check the method name for typos, or make sure you're using ':' (colon) for method calls, not '.' (dot).
[Lua] -> Script has been disabled for this entity to prevent further errors.
El sistema traduce los errores comunes de Lua a lenguaje claro:
| Error Lua sin procesar | Lo que la consola te indica |
|---|---|
attempt to call nil | Intentaste llamar a un método o función que no existe. Revisa errores tipográficos y el uso de : vs. .. |
index expected, got nil | Intentaste acceder a un campo en algo que es nil. Verifica que el código anterior lo haya inicializado. |
attempt to index | Intentaste acceder a una propiedad en un valor nil o inválido. |
bad argument | Una función recibió el tipo de argumento incorrecto. El mensaje muestra el tipo esperado vs. el real. |
| Límite superado | Lua instruction budget exceeded (250000 instruction limit) o Lua CPU-time budget exceeded (50ms current-thread CPU limit): la llamada se interrumpe durante la ejecución y se desactiva la instancia del script. |
Siempre lee el mensaje de error [Lua] completo antes de sumergirte en el código. Generalmente te señala directamente la solución.
No supongas que existen métodos no documentados
La API Lua de FMM expone un conjunto específico de métodos. No asumas que existen nombres abreviados o alternativos. Errores comunes:
context.prop:get_location()-- no existe. Usacontext.prop.current_location(un campo, no un método).context.prop:set_animation("open")-- no existe. Usacontext.prop:play_animation("open", true, true).context.event:setCancelled(true)-- no existe. Usacontext.event.cancel().context.cooldowns:check_global(...)-- no existe. Usacontext.cooldowns:global_ready()ycontext.cooldowns:set_global(ticks)para los tiempos de reutilización globales.context.player-- solo está definido en los eventos de elementos decorativos que involucran a un jugador, como clics, entradas o salidas de zonas vigiladas yon_projectile_hitcuando un jugador disparó el proyectil. No es un campo genérico de propietario y seránilen los eventos del ciclo de vida y en las funciones programadas.context.boss-- no existe. Eso es de EliteMobs. Usacontext.propen FMM.
Consulta los métodos admitidos en la API de Props y en las referencias compartidas que enlaza. Un nombre que parezca razonable no demuestra que el método exista.
Consejos de depuración
1. Usa context.log:info() con frecuencia
Agrega mensajes de registro en cada paso al depurar:
on_right_click = function(context)
context.log:info("Right click received!")
context.log:info("Event present: " .. tostring(context.event ~= nil))
context.log:info("State is_open: " .. tostring(context.state.is_open))
end
2. Revisa la consola al inicio
Añade context.log:info("Prop script started") a on_spawn y coloca un elemento nuevo después de recargar. Busca tu mensaje y los posibles errores de script o configuración en la consola. FMM no emite una línea de éxito por cada vinculación.
3. Verifica la ruta del archivo del modelo
La configuración .yml debe ser un archivo hermano del archivo del modelo (mismo directorio, mismo nombre base). Verifícalo revisando:
- Ruta del archivo del modelo:
plugins/FreeMinecraftModels/models/my_model.fmmodel - Ruta del archivo de configuración:
plugins/FreeMinecraftModels/models/my_model.yml
4. Prueba los eventos de script por separado
Al construir un script complejo, comienza probando cada hook por separado. Agrega un hook a la vez y verifica que se active antes de pasar al siguiente.
5. Busca errores tipográficos en los nombres de eventos
La causa más común de que un evento no se ejecute es un nombre mal escrito. Un evento desconocido provoca el rechazo del script durante la validación; consulta el error de la consola en lugar de esperar que se ejecuten los demás eventos del archivo.
Progresión para principiantes
Si quieres aprender este sistema desde cero, esta progresión funciona bien:
- Escribe un archivo con solo
api_version = 1yon_spawnque registre un mensaje. - Agrega el script a una configuración de prop y verifica que aparezca el mensaje de registro.
- Agrega
on_right_clicky registra cuando se hace clic en el prop. - Añade
on_left_clickconcontext.event.cancel()para suprimir el daño predeterminado del clic. - Reproduce una animación al hacer clic derecho.
- Agrega un sonido al hacer clic derecho.
- Agrega estado y comportamiento de alternancia.
- Agrega una vigilancia de zona para detección de proximidad.
- Solo entonces pasa a scripts complejos con múltiples hooks y schedulers.
Cada paso se basa en el anterior, y puedes probar en cada etapa.
Siguientes pasos
- Primeros Pasos -- estructura de archivos, hooks, tutorial del primer script, plantillas para copiar y pegar
- API de Props -- referencia completa de la API
- Ejemplos y Patrones -- scripts funcionales completos que puedes estudiar y adaptar