Aller au contenu principal

Scripts Lua : Dépannage

Cette page couvre les problèmes courants que vous pouvez rencontrer lors de l'écriture ou du débogage de scripts de props et d'objets FreeMinecraftModels, ainsi que des astuces pour travailler avec le système de génération paresseuse de configuration. Si vous cherchez des exemples fonctionnels, consultez Exemples et patterns. Si vous débutez, consultez Pour commencer.

Runtime Lua MagmaCore

FMM utilise le runtime ScriptInstance de MagmaCore. Pour la documentation du bac à sable FMM/MagmaCore, du planificateur, des zones, de l'API monde, des tables d'entités et des méthodes d'UI joueur, voir la page Moteur de scripts Lua MagmaCore. Les pouvoirs de boss et les scripts de PNJ d'EliteMobs réutilisent le bac à sable Lua mais exposent leurs propres tables de contexte.


Problèmes courants

1. Fichier de configuration non chargé / Script non attaché

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

Causes et solutions :

  • Aucun fichier de configuration .yml n'existe encore. FMM génère la configuration paresseusement lors du premier spawn du prop. La première fois qu'un modèle est généré, FMM crée la configuration .yml de manière asynchrone avec des valeurs par défaut (activé, pas de scripts). Vous devez éditer la configuration générée pour ajouter les noms de vos fichiers de script, puis refaire apparaître le prop.

  • 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/.


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

S'il y a une erreur Lua, la console affichera un bloc d'erreur [Lua] avec le nom du fichier, le numéro de ligne et une description.


4. Le hook ne se déclenche jamais

Symptôme : Le script se charge (vous voyez le message [FMM Scripts] Bound script), mais un hook spécifique 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
on_arrow_hiton_projectile_hit

Vérifiez également que le prop a bien une entité support d'armure sous-jacente. Certaines configurations de props peuvent ne pas produire d'entité cliquable.


5. Dépassement de délai / 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.


6. context.event est 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 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.

  • Les joueurs ne sont pas à portée du resource pack. L'animation est côté serveur, mais les joueurs ont besoin du resource pack FMM chargé pour voir le modèle.


8. Les particules n'apparaissent pas

  • Vérifiez que le nom de la particule est une valeur d'enum Bukkit Particle valide en MAJUSCULES : "FLAME", pas "flame".
  • 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 joue pas

  • Vérifiez que le nom du son est une constante d'enum Bukkit Sound valide en MAJUSCULES. Exemple : "BLOCK_NOTE_BLOCK_HARP", pas "block.note_block.harp".
  • 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 est par instance de prop et persiste pendant la durée de vie du prop. Si l'état semble se réinitialiser :

  • Le prop a peut-être été supprimé et refait apparaître (chaque spawn crée une nouvelle instance).
  • 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

Symptôme : L'objet personnalisé est dans la main du joueur, mais aucun hook d'objet ne se déclenche.

Causes et solutions :

  • Aucun champ material: dans la configuration YML. Les scripts d'objet ne fonctionnent que sur les modèles dont la configuration définit material:. Sans ce champ, FMM traite le modèle comme un prop, pas comme un objet personnalisé.

  • Clé PDC fmm_item_id manquante. L'objet dans l'inventaire du joueur doit porter la clé PersistentDataContainer fmm_item_id. Les objets obtenus via des commandes vanilla ou des plugins tiers peuvent ne pas avoir ce tag. Utilisez /fmm giveitem <id> ou le menu admin pour obtenir un objet correctement taggé.

  • Nom du fichier de script absent de la configuration. Vérifiez que la configuration .yml de l'objet contient bien le nom du fichier de script dans la liste scripts:, exactement comme pour les props.


13. Le script d'objet s'active puis se désactive immédiatement

Symptôme : Vous voyez le script se lier et se délier en succession rapide dans la console, ou on_equip se déclenche suivi immédiatement de on_unequip.

Cause : Le suivi d'équipement des objets se déclenche sur les événements de changement d'emplacement. Si vous vous donnez un objet par commande et qu'il atterrit dans un emplacement non actif, ou si votre emplacement actif change pendant la remise, le cycle équipement/déséquipement peut se déclencher coup sur coup.

Solution : Après vous être donné l'objet avec /fmm giveitem <id>, veillez à basculer manuellement vers l'emplacement d'inventaire contenant l'objet. Le script s'active selon l'emplacement actuellement sélectionné, et pas simplement selon la présence de l'objet dans l'inventaire.


14. Le hook se déclenche mais le script plante sur les méthodes d'entité

Symptôme : Un hook comme on_shift_right_click fonctionne, mais le script plante à l'intérieur d'un callback planifié avec des erreurs du type attempt to call nil lors de l'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 (porte-armures, objets au sol, orbes d'expérience, nuages d'effets de zone). Ces entités ne disposent pas des 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. Les données propres à l'événement manquent dans les callbacks planifiés

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 callbacks planifiés reçoivent un contexte neuf et ne conservent ni l'événement Bukkit d'origine ni l'acteur générique de zone. context.event est toujours nil dans un callback. Les scripts d'objet peuvent tout de même résoudre context.player depuis le propriétaire de l'objet tant que le script est vivant, mais les scripts de prop ne reçoivent un context.player de premier niveau que pendant les hooks pilotés par le joueur, tels que les clics et les entrées/sorties de zone. Les scripts de prop devraient capturer le joueur depuis context.player ou context.event.player pendant le hook d'origine s'ils en ont besoin plus tard.

Solution : Utilisez le contexte neuf du callback pour l'accès normal à l'objet, au monde et à l'état. Si vous avez besoin du joueur ou de l'entité exacts de l'événement d'origine, capturez-les avant de planifier et vérifiez qu'ils sont toujours 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)
-- 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

Comment fonctionne la génération paresseuse de configuration

Comprendre le système de configuration paresseuse aide à éviter la confusion lors de la mise en place 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 et refaire apparaître : Après que FMM a créé la configuration, vous l'éditez pour ajouter les noms de vos fichiers de script. La prochaine fois que le prop apparaît, les scripts seront chargés.

  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
Flux de travail rapide de configuration
  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. Refaites apparaître le prop -- le script est maintenant actif

Lire les messages d'erreur

Lorsque quelque chose ne fonctionne pas dans un script Lua de prop, la console affiche un bloc d'erreur [Lua]. Ces messages vous indiquent exactement quel fichier, quelle ligne, quel hook et ce qui s'est passé en langage clair.

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 -- n'est défini que pour les hooks de props FMM impliquant un joueur, tels que les hooks de clic et les hooks d'entrée/sortie de zone surveillée. Ce n'est pas un champ générique de propriétaire de prop : il vaudra nil dans les hooks de cycle de vie du prop et dans les callbacks du scheduler. Les scripts d'objet résolvent context.player depuis le propriétaire de l'objet.
  • context.boss -- n'existe pas. C'est pour EliteMobs. Utilisez context.prop dans FMM.

En cas de doute, consultez la page API Prop. Si ce n'est pas documenté là-bas, ça n'existe pas.


Astuces de débogage

1. Utilisez context.log:info() généreusement

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

on_right_click = function(context)
context.log:info("Clic droit reçu !")
context.log:info("Événement présent : " .. tostring(context.event ~= nil))
context.log:info("État is_open : " .. tostring(context.state.is_open))
end

2. Vérifiez la console au démarrage

FMM affiche [FMM Scripts] Bound script 'X' to prop 'Y' pour chaque liaison réussie. Si vous ne voyez pas ce message, la configuration ou le script n'a pas été chargé.

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 les fautes de frappe dans les noms de hooks

La raison la plus courante pour laquelle un hook ne se déclenche pas est un nom de hook mal orthographié. Le script se chargera sans erreurs, mais la fonction de hook mal orthographiée sera rejetée lors de la validation.


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 l'invulnérabilité.
  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