Saltar al contenido principal

Scripting Lua: Solución de Problemas

Esta página cubre los problemas comunes que puedes encontrar al escribir o depurar scripts de props e ítems de FreeMinecraftModels, además de consejos para trabajar con el sistema de generación diferida de configuración. Si buscas ejemplos funcionales, consulta Ejemplos y Patrones. Si recién estás comenzando, consulta Primeros Pasos.

MagmaCore Lua Runtime

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 Comunes

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 de configuración .yml. FMM genera la configuración de forma diferida en el primer spawn del prop. La primera vez que se genera un modelo, FMM crea la configuración .yml de forma asíncrona con valores predeterminados (habilitado, sin scripts). Debes editar la configuración generada para agregar los nombres de tus archivos de script y luego volver a generar el prop.

  • La configuración tiene isEnabled: false. Abre el archivo .yml junto al archivo del modelo y establece isEnabled: 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 .yml no 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.fmmodel necesita torch_01.yml en 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.

  • Nombre de archivo no coincide. El nombre en la configuración .yml debe coincidir exactamente con el nombre del archivo en la carpeta scripts/, incluyendo mayúsculas/minúsculas y la extensión .lua.

  • Archivo no presente. Verifica que el archivo .lua realmente exista en la carpeta scripts/.


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 end para cerrar una función o un bloque if
  • Paréntesis no emparejados
  • Falta coma entre definiciones de hooks en la tabla retornada
  • El archivo no termina con .lua

Si hay un error de Lua, la consola imprimirá un bloque de error [Lua] con el nombre del archivo, el número de línea y una descripción.


4. El hook nunca se activa

Síntoma: El script se carga (ves el mensaje [FMM Scripts] Bound script), pero un hook específico nunca se activa.

Verifica que el nombre del hook esté escrito exactamente como aparece en la referencia de hooks. Errores comunes:

Nombre incorrectoNombre correcto
on_clickon_right_click o on_left_click
on_interacton_right_click
on_hiton_left_click
on_tickon_game_tick
on_removeon_destroy
on_arrow_hiton_projectile_hit

Verifica también que el prop tenga realmente una entidad armor stand asociada. Algunas configuraciones de props pueden no producir una entidad que se pueda hacer clic.


5. Tiempo de espera agotado / Presupuesto de ejecución excedido

Síntoma: La consola muestra uno de dos mensajes.

Una llamada que se pasó del presupuesto dentro de la VM se aborta a mitad de ejecución:

Lua execution budget exceeded (50ms or 250000 instructions)

Una llamada que sí retornó pero que en conjunto tardó demasiado se detecta a posteriori:

[Lua] my_script.lua took 73ms in 'on_game_tick' (limit: 50ms) -- script disabled to prevent lag.

Cada hook, callback y evaluación de archivo puede ejecutar como máximo 250,000 instrucciones de Lua o durar 50 ms, lo que ocurra primero, y las llamadas anidadas comparten una única asignación. El script estaba realizando demasiado trabajo en una sola llamada de hook. Causas comunes:

  • Iterar sobre demasiadas entidades en on_game_tick
  • Crear demasiadas partículas por tick
  • Ejecutar bucles costosos sin distribuir el trabajo entre ticks

Solución: Mueve el trabajo pesado detrás de context.cooldowns o usa scheduler:run_repeating() con un intervalo razonable en lugar de on_game_tick.


6. context.event es nil en un hook 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 .bbmodel o .fmmodel para 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.

  • Los jugadores no están en el rango del resource pack. La animación es del lado del servidor, pero los jugadores necesitan tener cargado el resource pack de FMM para ver el modelo.


8. Las partículas no aparecen

  • Verifica que el nombre de la partícula sea un valor válido del enum Particle de Bukkit en MAYÚSCULAS: "FLAME", no "flame".
  • Verifica que la ubicación esté en un chunk cargado. Si no hay jugadores cerca, el chunk puede estar descargado.
  • Verifica que count sea al menos 1.
  • Algunas partículas (como DUST) necesitan datos adicionales específicos que el spawn_particle() básico puede no soportar. Usa partículas estándar como FLAME, HEART, HAPPY_VILLAGER, NOTE, ENCHANT, etc.

9. El sonido no se reproduce

  • Verifica que el nombre del sonido sea una constante válida del enum Sound de Bukkit en MAYÚSCULAS. Ejemplo: "BLOCK_NOTE_BLOCK_HARP", no "block.note_block.harp".
  • 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 es por instancia de prop y persiste durante la vida del prop. Si el estado parece reiniciarse:

  • El prop puede haber sido eliminado y vuelto a generar (cada spawn crea una nueva instancia).
  • 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 librería 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 ítem no se activa

Síntoma: El ítem personalizado está en la mano del jugador, pero no se dispara ningún hook de ítem.

Causas y soluciones:

  • Falta el campo material: en la configuración YML. Los scripts de ítem solo funcionan en modelos que tienen material: establecido en su configuración. Sin ese campo, FMM trata el modelo como un prop, no como un ítem personalizado.

  • Falta la clave PDC fmm_item_id. El ítem en el inventario del jugador debe tener la clave fmm_item_id en su PersistentDataContainer. Los ítems obtenidos mediante comandos vanilla o plugins de terceros pueden carecer de esa etiqueta. Usa /fmm giveitem <id> o el menú de administración para obtener un ítem correctamente etiquetado.

  • El nombre del archivo de script no está listado en la configuración. Verifica que el .yml del ítem tenga el nombre del archivo de script en la lista scripts:, igual que con los props.


13. El script de ítem se activa y se desactiva de inmediato

Síntoma: Ves el script vincularse y desvincularse en rápida sucesión en la consola, o on_equip se dispara seguido inmediatamente de on_unequip.

Causa: El seguimiento de equipamiento de ítems se dispara con los eventos de cambio de ranura. Si te das a ti mismo un ítem con un comando y va a parar a una ranura no activa, o si tu ranura activa cambia durante la entrega, el ciclo equipar/desequipar puede dispararse en rápida sucesión.

Solución: Después de darte el ítem con /fmm giveitem <id>, asegúrate de cambiar manualmente a la ranura del inventario que contiene el ítem. El script se activa en función de qué ranura está seleccionada actualmente, no solo de si el ítem está en el inventario.


14. El hook se dispara pero el script falla en los métodos de entidad

Síntoma: Un hook como on_shift_right_click funciona, pero el script se cae dentro de un callback programado 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 en rango, incluidas las no vivas (armor stands, ítems soltados, orbes de experiencia, nubes de efecto de área). Esas entidades no tienen métodos de entidad viva 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. Los datos exclusivos del evento faltan en los callbacks programados

Síntoma: Los datos del evento de clic/combate faltan dentro de un callback de scheduler:run_later() o scheduler:run_repeating().

Causa: Los callbacks programados reciben un context nuevo y no conservan el evento de Bukkit original ni el actor genérico de zona. context.event siempre es nil en los callbacks. Los scripts de ítem sí pueden resolver context.player a partir del propietario del ítem mientras el script siga vivo, pero los scripts de prop solo reciben un context.player de nivel superior durante los hooks impulsados por el jugador, como clics y entrada/salida de zona. Los scripts de prop deberían capturar el jugador desde context.player o context.event.player durante el hook original cuando lo vayan a necesitar después.

Solución: Usa el context nuevo del callback para el acceso normal a ítem/mundo/estado. Si necesitas el jugador o la entidad exactos del evento original, captúralos antes de programar 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)
-- Item scripts can use later_context.player here.
-- 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:

  1. Primer spawn: Cuando un prop aparece y no existe un archivo .yml asociado, 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.

  2. Valores predeterminados: La configuración generada tiene isEnabled: true y una lista vacía scripts: [].

  3. 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.

  4. Editar y volver a generar: Después de que FMM cree la configuración, la editas para agregar los nombres de tus archivos de script. La próxima vez que el prop aparezca, los scripts se cargarán.

  5. Ubicación de la configuración: El archivo .yml se 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
Flujo de trabajo de configuración rápida
  1. Coloca el modelo en models/
  2. Coloca el script en scripts/
  3. Genera el prop una vez (genera el .yml)
  4. Edita el .yml para agregar scripts: [my_script.lua]
  5. Vuelve a generar el prop -- el script ahora está activo

Lectura de Mensajes de Error

Cuando algo sale mal en un script Lua de prop, la consola imprime un bloque de error [Lua]. Estos mensajes te indican exactamente qué archivo, qué línea, qué hook y qué salió mal en lenguaje claro.

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 procesarLo que la consola te indica
attempt to call nilIntentaste llamar a un método o función que no existe. Revisa errores tipográficos y el uso de : vs. ..
index expected, got nilIntentaste acceder a un campo en algo que es nil. Verifica que el código anterior lo haya inicializado.
attempt to indexIntentaste acceder a una propiedad en un valor nil o inválido.
bad argumentUna función recibió el tipo de argumento incorrecto. El mensaje muestra el tipo esperado vs. el real.
TimeoutTu script tardó Xms en 'hook_name' (límite: 50ms) -- script deshabilitado para prevenir lag.
consejo

Siempre lee el mensaje de error [Lua] completo antes de sumergirte en el código. Generalmente te señala directamente la solución.


No Asumas 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. Usa context.prop.current_location (un campo, no un método).
  • context.prop:set_animation("open") -- no existe. Usa context.prop:play_animation("open", true, true).
  • context.event:setCancelled(true) -- no existe. Usa context.event.cancel().
  • context.cooldowns:check_global(...) -- no existe. Usa context.cooldowns:global_ready() y context.cooldowns:set_global(ticks) para los tiempos de reutilización globales.
  • context.player -- solo está definido en los hooks de prop de FMM que involucran a un jugador, como los hooks de clic y los de entrada/salida de zona vigilada. No es un campo genérico de propietario del prop y será nil en los hooks de ciclo de vida del prop y en los callbacks del planificador. Los scripts de ítem resuelven context.player a partir del propietario del ítem.
  • context.boss -- no existe. Eso es de EliteMobs. Usa context.prop en FMM.

En caso de duda, consulta la página de API de Props. Si no está documentado allí, no existe.


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

FMM registra [FMM Scripts] Bound script 'X' to prop 'Y' por cada vinculación exitosa. Si no ves este mensaje, la configuración o el script no se cargaron.

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 hooks individualmente

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 hooks

La razón más común por la que un hook no se activa es un nombre de hook mal escrito. El script se cargará sin errores, pero la función de hook mal escrita será rechazada durante la validación.


Ruta de Progresión para Principiantes

Si quieres aprender este sistema desde cero, esta progresión funciona bien:

  1. Escribe un archivo con solo api_version = 1 y on_spawn que registre un mensaje.
  2. Agrega el script a una configuración de prop y verifica que aparezca el mensaje de registro.
  3. Agrega on_right_click y registra cuando se hace clic en el prop.
  4. Agrega on_left_click con context.event.cancel() para invulnerabilidad.
  5. Reproduce una animación al hacer clic derecho.
  6. Agrega un sonido al hacer clic derecho.
  7. Agrega estado y comportamiento de alternancia.
  8. Agrega una vigilancia de zona para detección de proximidad.
  9. 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.


Próximos 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