Aller au contenu principal

Scripts Lua : Dépannage

Cette page présente les problèmes courants lors de l'écriture ou du débogage des scripts de props FreeMinecraftModels, ainsi que des conseils sur la génération de configuration à la demande. Pour des scripts fonctionnels, consultez les exemples et méthodes. Si vous débutez, consultez Pour commencer.

Moteur Lua MagmaCore

FMM utilise le moteur ScriptInstance de MagmaCore. Pour le bac à sable FMM/MagmaCore, le planificateur, les zones, l'API du monde, les tables d'entités et l'interface joueur, consultez le moteur de scripts Lua MagmaCore. Les pouvoirs de boss et les scripts de PNJ EliteMobs réutilisent le bac à sable Lua, mais exposent leurs propres tables de contexte.


Problèmes courants​

1. Configuration non chargée ou script non attaché​

Symptôme : Le prop apparaît mais ne réagit pas aux clics ni à aucun hook.

Causes et solutions :

  • Aucun fichier .yml n'existe encore. FMM tente de le créer de façon asynchrone à la première apparition du prop, avec les valeurs par défaut (activé, aucun script), sans écraser un fichier existant. Ajoutez les noms des scripts, réussissez /fmm reload, puis placez un nouveau prop. Les modifications YAML et Lua nécessitent toutes deux un rechargement.

  • La configuration a isEnabled: false. Ouvrez le fichier .yml à côté du fichier de modèle et définissez isEnabled: true.

  • La liste scripts: est vide. Ajoutez les noms de vos fichiers de script :

    isEnabled: true
    scripts:
    - my_script.lua
  • Le nom du fichier .yml ne correspond pas au nom du fichier de modèle. La configuration doit avoir le même nom de base que le fichier de modèle. Par exemple, torch_01.fmmodel nécessite torch_01.yml dans le même répertoire.


2. Fichier de script introuvable​

Symptôme : La console affiche : [FMM Scripts] Script 'my_script.lua' not found in scripts/ folder

Causes et solutions :

  • Mauvais répertoire. Les fichiers de script doivent être dans plugins/FreeMinecraftModels/scripts/, pas à côté du fichier de modèle.

  • Nom de fichier incorrect. Le nom dans la configuration .yml doit correspondre au nom de fichier dans le dossier scripts/, casse comprise. L'extension .lua est recommandée dans la configuration mais optionnelle ; FMM ajoute .lua en interne si vous l'omettez.

  • Fichier non présent. Vérifiez bien que le fichier .lua existe réellement dans le dossier scripts/.

  • Définition en cache ou collision de noms. FMM recherche les scripts récursivement à l'initialisation du fournisseur, mais les lie par nom de fichier, pas par chemin relatif. Gardez des noms uniques entre sous-dossiers. Après ajout ou modification d'un fichier Lua, exécutez un /fmm reload réussi ; faire réapparaître un prop ne relit pas le code Lua en cache.


3. Le script ne se charge pas du tout​

Symptôme : Pas d'erreur console, mais aucun hook ne se déclenche.

Vérifiez la console du serveur pour les erreurs de syntaxe Lua au démarrage. Les causes les plus courantes sont :

  • end manquant pour fermer une fonction ou un bloc if
  • Parenthèses non appariées
  • Virgule manquante entre les définitions de hooks dans la table retournée
  • Le fichier ne se termine pas par .lua

Les échecs de découverte apparaissent sous forme d'avertissements de scripts invalides ou retirés, ou d'erreurs de chargement. Les erreurs de hooks et de fonctions planifiées utilisent un bloc [Lua] avec le fichier et le contexte d'exécution ; le numéro de ligne n'apparaît que s'il peut être extrait de l'exception.


4. Le hook ne se déclenche jamais​

Symptôme : Votre propre message de démarrage apparaît, mais un hook précis ne se déclenche jamais.

Vérifiez que le nom du hook est orthographié exactement comme listé dans la référence des hooks. Erreurs courantes :

Mauvais nomNom correct
on_clickon_right_click ou on_left_click
on_interacton_right_click
on_hiton_left_click
on_tickon_game_tick
on_removeon_destroy (ne s'exécute que lorsque le prop est retiré définitivement, pas lors d'un déchargement de chunk ni d'un rechargement)
on_arrow_hiton_projectile_hit

Vérifiez que le modèle est chargé, possède une hitbox utilisable et qu'aucun écouteur de protection n'a annulé l'interaction avant le callback du prop. Un prop utilise un porte-armure sous-jacent ; changer une option ne rend pas cliquable un modèle invalide ou non chargé.


5. Délai ou budget d'exécution dépassé​

Symptôme : La console affiche une erreur Lua levée depuis l'intérieur de la VM. Un appel qui a dépassé le budget est interrompu en pleine exécution avec l'un de ces messages :

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)

Chaque hook, callback et évaluation de fichier peut exécuter au maximum 250 000 instructions Lua ou consommer 50 ms de temps CPU du thread courant, selon ce qui survient en premier, et les appels imbriqués partagent une allocation unique. (Le troisième message n'apparaît que sur une JVM où la mesure du temps CPU par thread n'est pas disponible ; MagmaCore utilise alors une limite plus généreuse de 250 ms de temps écoulé.) Le script effectuait trop de travail dans un seul appel de hook. Causes courantes :

  • Itérer sur trop d'entités dans on_game_tick
  • Créer trop de particules par tick
  • Exécuter des boucles coûteuses sans répartir le travail sur plusieurs ticks

Solution : Déplacez le travail lourd derrière context.cooldowns ou utilisez scheduler:run_repeating() avec un intervalle raisonnable au lieu de on_game_tick.

La limite d'instructions borne les boucles Lua. Le temps est vérifié toutes les 1 024 instructions Lua et au retour ; ces vérifications n'interrompent pas une méthode Java/Bukkit bloquante en cours d'appel. Limitez aussi le travail de chaque fonction planifiée.


6. context.event vaut nil dans un hook de clic​

Cela ne devrait normalement pas se produire pour on_left_click et on_right_click, mais protégez-vous toujours avec :

if context.event then
context.event.cancel()
end

Dans les callbacks de scheduler, context.event est toujours nil -- c'est le comportement attendu. La modification d'événements ne peut se faire que dans le hook d'événement lui-même.


7. L'animation ne se joue pas​

Symptôme : play_animation() renvoie false, ou rien de visible ne se passe.

Causes et solutions :

  • Mauvais nom d'animation. Le nom doit correspondre exactement à ce qui est défini dans le fichier de modèle. Vérifiez votre fichier .bbmodel ou .fmmodel pour le nom d'animation correct.

  • Le modèle n'a pas d'animations. Tous les modèles n'ont pas d'animations. Vérifiez que le fichier de modèle contient réellement des données d'animation.

  • Le joueur n'a pas le pack ou se trouve hors de la distance d'affichage du modèle. Il lui faut à la fois le resource pack actuel et un modèle visible.

  • La demande est en attente ou supprimée. Passer true pour blend utilise un seul emplacement d'attente, pas un fondu. Une demande ultérieure peut le remplacer et une attaque pendant une animation personnalisée peut renvoyer true sans l'interrompre. Utilisez les arguments positionnels de la page Animations et distinguez une demande acceptée d'une image réellement affichée.


8. Les particules n'apparaissent pas​

  • Les noms de particules acceptent FLAME et flame ; l’API normalise la casse avant de chercher la particule Bukkit.
  • Vérifiez que l'emplacement est dans un chunk chargé. Si aucun joueur n'est à proximité, le chunk peut être déchargé.
  • Vérifiez que count est au moins 1.
  • Certaines particules (comme DUST) nécessitent des données supplémentaires spécifiques que la base spawn_particle() peut ne pas supporter. Utilisez des particules standard comme FLAME, HEART, HAPPY_VILLAGER, NOTE, ENCHANT, etc.

9. Le son ne se joue pas​

  • Les sons acceptent les noms enum Bukkit sans distinction de casse ou des clés valides comme minecraft:block.note_block.harp et les clés des packs personnalisés. Le client doit disposer du son dans son pack de ressources.
  • Vérifiez que les coordonnées de position sont correctes (pas tous des zéros ou NaN).
  • Vérifiez que le volume est supérieur à 0.

10. L'état se réinitialise de manière inattendue​

context.state appartient à une instance de script. Les entrées distinctes de la liste scripts: d'un même prop ne le partagent pas. Il dure tant que l'instance reste active. Si l'état semble se réinitialiser :

  • Le prop a peut-être été supprimé et refait apparaître, ou son chunk a été déchargé puis rechargé. Chaque apparition ou chargement de chunk crée une nouvelle instance. Stockez les valeurs qui doivent survivre à cela avec prop:set_persistent_data.
  • Un rechargement réussi ou une erreur de script a pu arrêter l'ancienne instance. Consultez la console ; corriger le fichier ne réactive pas une instance arrêtée sans rechargement et nouvelle liaison.
  • Vous lisez peut-être l'état dans un callback de scheduler en utilisant la mauvaise variable de contexte. Utilisez toujours le paramètre de contexte propre au callback.

11. La bibliothèque os n'est pas disponible​

Symptôme : Le script plante avec une erreur du type attempt to index nil (os).

La bibliothèque os est entièrement retirée du bac à sable Lua. Vous ne pouvez utiliser ni os.time(), ni os.clock(), ni aucune autre fonction os.

Solution : Utilisez context.world:get_time() pour l'heure du monde, ou suivez le temps écoulé manuellement à l'aide de drapeaux d'état et de compteurs de ticks dans on_game_tick. Pour les temps de recharge, préférez context.cooldowns:check_local(key, ticks).


12. Le script d'objet ne s'active pas​

Le format des scripts d'objets équipés a été retiré. Un objet personnalisé avec une liste scripts: non vide ou des enchantements anciens sans espace de noms est ignoré et signalé dans les journaux. Remplacez-le par une définition actuelle d'objet personnalisé et des enchantements avec espace de noms. Les scripts de props utilisent toujours scripts:.

13. Définition d'objet indisponible​

Consultez l'avertissement au démarrage ou au rechargement pour connaître le fichier exact. Un matériau indisponible, par exemple un type d'objet récent sur un ancien serveur, entraîne aussi l'exclusion de l'objet. Corrigez ou remplacez la définition pour votre version de serveur ; FMM ne la réécrit pas et n'invente pas de matériau de remplacement.

14. Le hook fonctionne, mais le script échoue sur les méthodes d'entité​

Symptôme : Un hook comme on_right_click fonctionne, mais une fonction planifiée échoue avec attempt to call nil lors d'un appel à entity:damage() ou entity:push().

Cause : context.world:get_nearby_entities() renvoie TOUTES les entités à portée, y compris les entités non vivantes : objets au sol, orbes d'expérience et nuages d'effets de zone. Elles n'ont pas de méthodes d'entité vivante telles que damage(), push() ou add_potion_effect().

Solution : Protégez-vous toujours avec if entity.damage then avant d'appeler des méthodes d'entité vivante :

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. Données d'événement absentes des fonctions planifiées​

Symptôme : Les données de l'événement de clic ou de combat sont absentes à l'intérieur d'un callback scheduler:run_later() ou scheduler:run_repeating().

Cause : Les fonctions planifiées reçoivent un contexte neuf sans l'événement Bukkit d'origine ni l'acteur générique de zone. context.event y vaut toujours nil. Les scripts de props ne reçoivent context.player au premier niveau que dans les hooks déclenchés par un joueur, comme les clics ou les entrées et sorties de zone. S'ils ont besoin du joueur plus tard, ils doivent le capturer depuis context.player ou context.event.player pendant le hook initial.

Solution : Utilisez le contexte neuf pour accéder normalement au monde et à l'état. Si vous avez besoin du joueur ou de l'entité de l'événement initial, capturez-les avant de planifier, puis vérifiez qu'ils sont encore valides.

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

Fonctionnement de la génération de configuration à la demande​

Comprendre la génération de configuration à la demande évite des difficultés lors de la préparation de nouveaux props :

  1. Premier spawn : Lorsqu'un prop apparaît et qu'aucun fichier .yml adjacent n'existe, FMM crée la configuration de manière asynchrone. Cela signifie que le fichier est écrit dans un thread en arrière-plan et n'est pas immédiatement disponible.

  2. Valeurs par défaut : La configuration générée a isEnabled: true et une liste scripts: [] vide.

  3. Pas de scripts au premier spawn : Comme la configuration est créée après que le prop a déjà été généré (et n'a pas de scripts listés), le prop n'aura pas de scripts attachés lors de son premier spawn.

  4. Éditer, recharger et refaire apparaître : Ajoutez les noms des scripts, puis réussissez /fmm reload. Les réglages, les vérifications de placement et les listes de scripts du menu utilisent le même catalogue préparé. Un nouveau prop utilise ce catalogue sans relire le YAML modifié. Les modifications Lua nécessitent aussi un rechargement.

  5. Emplacement de la configuration : Le fichier .yml est créé dans le même répertoire que le fichier de modèle, avec le même nom de base. Par exemple :

    • Modèle : plugins/FreeMinecraftModels/models/fountain.fmmodel
    • Configuration : plugins/FreeMinecraftModels/models/fountain.yml
Configuration rapide
  1. Placez le modèle dans models/
  2. Placez le script dans scripts/
  3. Faites apparaître le prop une fois (génère le .yml)
  4. Éditez le .yml pour ajouter scripts: [my_script.lua]
  5. Rechargez pour découvrir le fichier Lua, placez un nouveau prop et vérifiez son propre message de démarrage

Lire les messages d'erreur​

Les erreurs de hooks et de fonctions planifiées produisent un bloc [Lua] indiquant le fichier et le contexte d'exécution. Le numéro de ligne est inclus si l'exception contient un emplacement source reconnaissable. Les erreurs de découverte et de configuration peuvent utiliser d'autres avertissements.

Un message d'erreur typique ressemble à ceci :

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

Le système traduit les erreurs Lua courantes en langage clair :

Erreur Lua bruteCe que la console vous dit
attempt to call nilVous avez essayé d'appeler une méthode ou fonction qui n'existe pas. Vérifiez les fautes de frappe et l'utilisation de : vs ..
index expected, got nilVous avez essayé d'accéder à un champ sur quelque chose qui est nil. Vérifiez que le code précédent l'a initialisé.
attempt to indexVous avez essayé d'accéder à une propriété sur une valeur nil ou invalide.
bad argumentUne fonction a reçu le mauvais type d'argument. Le message montre le type attendu vs. réel.
Budget dépasséLua instruction budget exceeded (250000 instruction limit) ou Lua CPU-time budget exceeded (50ms current-thread CPU limit) -- l'appel a été interrompu en pleine exécution et l'instance de script désactivée.
astuce

Lisez toujours le message d'erreur [Lua] complet avant de plonger dans le code. Il vous oriente généralement directement vers la solution.


Ne supposez pas que des méthodes non documentées existent​

L'API Lua FMM expose un ensemble spécifique de méthodes. Ne supposez pas que des raccourcis ou des noms alternatifs existent. Erreurs courantes :

  • context.prop:get_location() -- n'existe pas. Utilisez context.prop.current_location (un champ, pas une méthode).
  • context.prop:set_animation("open") -- n'existe pas. Utilisez context.prop:play_animation("open", true, true).
  • context.event:setCancelled(true) -- n'existe pas. Utilisez context.event.cancel().
  • context.cooldowns:check_global(...) -- n'existe pas. Utilisez context.cooldowns:global_ready() et context.cooldowns:set_global(ticks) pour les temps de recharge globaux.
  • context.player : uniquement défini dans les hooks de props FMM impliquant un joueur, comme les clics, les entrées ou sorties de zones surveillées, et on_projectile_hit lorsqu'un joueur a tiré le projectile. Il ne désigne pas un propriétaire générique du prop et vaut nil dans les hooks de cycle de vie et les fonctions planifiées.
  • context.boss -- n'existe pas. C'est pour EliteMobs. Utilisez context.prop dans FMM.

Consultez les méthodes prises en charge dans l'API Prop et ses références partagées. Un nom plausible ne prouve pas l'existence d'une méthode.


Conseils de débogage​

1. Utilisez régulièrement context.log:info()​

Ajoutez des messages de log à chaque étape lors du débogage :

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. Vérifiez la console au démarrage​

Ajoutez context.log:info("Prop script started") à on_spawn, puis placez un nouveau prop après le rechargement. Recherchez ce message et les erreurs de script ou de configuration dans la console. FMM n'affiche pas de message de réussite pour chaque liaison.

3. Vérifiez le chemin du fichier de modèle​

La configuration .yml doit être adjacente au fichier de modèle (même répertoire, même nom de base). Vérifiez ceci en contrôlant :

  • Chemin du fichier de modèle : plugins/FreeMinecraftModels/models/my_model.fmmodel
  • Chemin du fichier de configuration : plugins/FreeMinecraftModels/models/my_model.yml

4. Testez les hooks individuellement​

Lors de la construction d'un script complexe, commencez par tester chaque hook isolément. Ajoutez un hook à la fois et vérifiez qu'il se déclenche avant de passer au suivant.

5. Vérifiez l'orthographe des noms de hooks​

La cause la plus courante d'un hook inactif est une faute dans son nom. Un hook inconnu entraîne le rejet du script lors de la validation. Consultez l'erreur de la console ; ne vous attendez pas à ce que les autres hooks du fichier s'exécutent.


Parcours de progression pour débutants​

Si vous voulez apprendre ce système à partir de zéro, cette progression fonctionne bien :

  1. Écrivez un fichier avec seulement api_version = 1 et on_spawn qui affiche un message de log.
  2. Ajoutez le script à une configuration de prop et vérifiez que le message de log apparaît.
  3. Ajoutez on_right_click et affichez un message quand le prop est cliqué.
  4. Ajoutez on_left_click avec context.event.cancel() pour supprimer les dégâts par défaut du clic.
  5. Jouez une animation au clic droit.
  6. Ajoutez un son au clic droit.
  7. Ajoutez l'état et le comportement de basculement.
  8. Ajoutez une surveillance de zone pour la détection de proximité.
  9. Ce n'est qu'après cela que vous passez aux scripts complexes multi-hooks avec des schedulers.

Chaque étape s'appuie sur la précédente, et vous pouvez tester à chaque stade.


Prochaines étapes​

  • Pour commencer -- structure de fichiers, hooks, guide du premier script, modèles à copier-coller
  • API Prop -- référence API complète
  • Exemples et patterns -- scripts complets fonctionnels que vous pouvez étudier et adapter