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.
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
.ymln'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éfinissezisEnabled: true. -
La liste
scripts:est vide. Ajoutez les noms de vos fichiers de script :isEnabled: true
scripts:
- my_script.lua -
Le nom du fichier
.ymlne 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.fmmodelnécessitetorch_01.ymldans 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
.ymldoit correspondre au nom de fichier dans le dossierscripts/, casse comprise. L'extension.luaest recommandée dans la configuration mais optionnelle ; FMM ajoute.luaen interne si vous l'omettez. -
Fichier non présent. Vérifiez bien que le fichier
.luaexiste réellement dans le dossierscripts/. -
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 reloadré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 :
endmanquant pour fermer une fonction ou un blocif- 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 nom | Nom correct |
|---|---|
on_click | on_right_click ou on_left_click |
on_interact | on_right_click |
on_hit | on_left_click |
on_tick | on_game_tick |
on_remove | on_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_hit | on_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
.bbmodelou.fmmodelpour 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
truepourblendutilise un seul emplacement d'attente, pas un fondu. Une demande ultérieure peut le remplacer et une attaque pendant une animation personnalisée peut renvoyertruesans 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
FLAMEetflame; 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
countest au moins 1. - Certaines particules (comme
DUST) nécessitent des données supplémentaires spécifiques que la basespawn_particle()peut ne pas supporter. Utilisez des particules standard commeFLAME,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.harpet 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 :
-
Premier spawn : Lorsqu'un prop apparaît et qu'aucun fichier
.ymladjacent 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. -
Valeurs par défaut : La configuration générée a
isEnabled: trueet une listescripts: []vide. -
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.
-
É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. -
Emplacement de la configuration : Le fichier
.ymlest 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
- Modèle :
- Placez le modèle dans
models/ - Placez le script dans
scripts/ - Faites apparaître le prop une fois (génère le
.yml) - Éditez le
.ymlpour ajouterscripts: [my_script.lua] - 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 brute | Ce que la console vous dit |
|---|---|
attempt to call nil | Vous 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 nil | Vous 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 index | Vous avez essayé d'accéder à une propriété sur une valeur nil ou invalide. |
bad argument | Une 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. |
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. Utilisezcontext.prop.current_location(un champ, pas une méthode).context.prop:set_animation("open")-- n'existe pas. Utilisezcontext.prop:play_animation("open", true, true).context.event:setCancelled(true)-- n'existe pas. Utilisezcontext.event.cancel().context.cooldowns:check_global(...)-- n'existe pas. Utilisezcontext.cooldowns:global_ready()etcontext.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, eton_projectile_hitlorsqu'un joueur a tiré le projectile. Il ne désigne pas un propriétaire générique du prop et vautnildans les hooks de cycle de vie et les fonctions planifiées.context.boss-- n'existe pas. C'est pour EliteMobs. Utilisezcontext.propdans 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 :
- Écrivez un fichier avec seulement
api_version = 1eton_spawnqui affiche un message de log. - Ajoutez le script à une configuration de prop et vérifiez que le message de log apparaît.
- Ajoutez
on_right_clicket affichez un message quand le prop est cliqué. - Ajoutez
on_left_clickaveccontext.event.cancel()pour supprimer les dégâts par défaut du clic. - Jouez une animation au clic droit.
- Ajoutez un son au clic droit.
- Ajoutez l'état et le comportement de basculement.
- Ajoutez une surveillance de zone pour la détection de proximité.
- 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