Script Lua : Dépannage
Cette page couvre les problèmes courants que vous pouvez rencontrer en écrivant ou en déboguant des pouvoirs Lua, ainsi que des conseils de migration pour les auteurs venant d'EliteScript. Si vous déboguez des scripts de PNJ, consultez Scripts de PNJ. Si vous cherchez des exemples fonctionnels, consultez Exemples & Modèles. Si vous débutez tout juste, consultez Premiers pas.
EliteMobs utilise le moteur de script Lua MagmaCore partagé entre les plugins Nightbreak. Pour la documentation sur les concepts partagés tels que le bac à sable (sandbox), le planificateur (scheduler), les zones, l'API de monde, les tables d'entités et les méthodes d'interface joueur, consultez la page Moteur de Script Lua MagmaCore.
Problèmes courants
1. Le pouvoir ne se charge pas du tout
Vérifiez la console du serveur à la recherche d'erreurs au démarrage du serveur. La cause la plus fréquente est une erreur de syntaxe Lua (end manquant, parenthèses non appariées, etc.). Vérifiez également que le fichier se termine par .lua et qu'il est placé dans le bon répertoire powers.
Pour les noms personnalisés, vérifiez si un fichier de métadonnées .yml ou .yaml du même répertoire et de même nom de base définit isEnabled: false ; cela empêche la découverte. Un fichier de métadonnées de même nom de base placé à côté d'un pouvoir fourni applique aussi ses champs isEnabled, effect, powerType et de temps de recharge à ce pouvoir. Si deux fichiers de pouvoir portent le même nom dans des dossiers différents, un seul se charge ; la console nomme la copie ignorée.
Le fichier doit renvoyer une table contenant api_version = 1. Cette table accepte uniquement api_version, le champ facultatif priority et les noms de hooks pris en charge ; les valeurs des hooks doivent être des fonctions. Gardez les fonctions utilitaires locales au lieu d'ajouter des clés arbitraires à la table renvoyée. Le code de premier niveau s'exécute lors de la validation de la définition et de l'initialisation du runtime, sans contexte de hook.
2. Le hook ne se déclenche jamais
Vérifiez que le nom du hook est orthographié exactement comme indiqué dans la liste des hooks. Erreurs courantes : on_boss_hit (faux) vs on_boss_damaged_by_player (correct), ou on_tick (faux) vs on_game_tick (correct).
Une clé non prise en charge dans la table renvoyée empêche le chargement de toute la définition. Si elle se charge mais ne s'exécute pas, vérifiez aussi que le boss référence le nom du fichier du pouvoir, que l'événement concerné se produit et que le runtime n'a pas échoué, été mis en pause ou subi une suppression de ses pouvoirs.
3. context.player est nil
Seuls les hooks dont l'événement sous-jacent porte un joueur renseignent context.player. Il vaut toujours nil dans on_spawn, on_game_tick, on_boss_damaged, on_boss_damaged_by_elite, on_exit_combat, on_heal, on_death, on_phase_switch et on_mind_action.
Il est renseigné dans on_boss_damaged_by_player, on_reinforcement_damaged_by_player, on_player_damaged_by_boss, on_enter_combat et on_boss_target_changed, ainsi que dans on_zone_enter / on_zone_leave lorsque l'entité concernée est un joueur. Vérifiez toujours qu'il n'est pas nil avant de l'utiliser. Voir Hooks et Cycle de Vie pour le tableau complet.
4. Délai dépassé / budget d'exécution dépassé
Chaque hook, callback planifié et évaluation de fichier partage avec ses appels imbriqués un budget de 250 000 instructions Lua et de 50 ms de temps CPU du thread. Si la JVM ne peut pas mesurer ce temps CPU, une limite de secours de 250 ms de temps écoulé s'applique. L'erreur indique la limite dépassée :
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)
La VM interrompt les boucles Lua incontrôlées pendant leur exécution. Elle ne peut pas interrompre un appel Java en cours, et aucun seuil distinct de 50 ms de temps réel ne s'applique après son retour. Les causes courantes comprennent le parcours de trop nombreuses entités, les particules dans une grande zone sans valeur coverage et le travail coûteux à chaque tick. Utilisez des cooldowns, moins de points d'échantillonnage ou context.scheduler:run_every(...) pour répartir le travail entre les ticks.
5. Le callback du planificateur utilise des données obsolètes
Vous utilisez probablement le context extérieur au lieu du paramètre du callback. Remplacez function() ... context.boss ... end par function(tick_context) ... tick_context.boss ... end.
Les callbacks planifiés reçoivent un nouveau contexte sans l'événement déclencheur, sa cible directe ni son joueur. Capturez explicitement une cible nécessaire avant la planification et vérifiez qu'elle reste valide au moment de l'utiliser. Relisez les positions actuelles et les autres propriétés variables au lieu de conserver une ancienne table.
6. La requête de zone ne renvoie aucune entité
Vérifiez bien la définition de la zone. Pour les zones natives, assurez-vous que kind est en minuscules ("sphere", pas "SPHERE"). Pour les utilitaires de script, assurez-vous que shape est en majuscules ("CONE", pas "cone"). Vérifiez aussi que origin ou Target se résout réellement vers un emplacement valide.
Vérifiez aussi le filtre d'entités et le monde. Les requêtes natives de zone du boss utilisent living par défaut, excluent le boss et cherchent dans son monde. Les zones des utilitaires de script utilisent PLAYER par défaut. Obtenez les cibles de zone Lua avec full_target() ou border_target() de l'objet de zone ; un simple context.script:target({targetType = "ZONE_FULL"}) n'a aucune zone associée.
7. Les particules n'apparaissent pas
Utilisez un nom valide de l'énumération Bukkit Particle ; "FLAME" et "flame" sont normalisés en majuscules. Vérifiez la position, la quantité et les données requises. BLOCK exige des données de bloc que cet utilitaire du monde ne fournit pas : utilisez une particule prise en charge ou l'API de particules de script avec son option de matériau.
8. Le délai de récupération ne semble pas fonctionner
check_local(key, duration) vérifie et définit le délai en un seul appel. Vous pouvez aussi utiliser local_ready(key), puis set_local(duration, key) quand l'attaque commence ; vérifier la disponibilité seule ne démarre jamais le délai. Les pouvoirs du boss partagent les clés explicites : utilisez donc un préfixe propre à chaque pouvoir. Les métadonnées YAML ne bloquent pas automatiquement l'exécution des hooks Lua.
9. Le boss continue d'utiliser un pouvoir après sa mort
Les tâches de context.scheduler sont annulées quand le runtime du pouvoir se ferme. Annulez les boucles d'attaque dans on_exit_combat si elles doivent s'arrêter avant le retrait du boss. Vérifiez aussi l'utilitaire appelé : certains utilitaires Java démarrent leurs propres tâches. Par exemple, world:run_fake_gold_nugget_damage(...) exécute déjà une boucle à chaque tick avec son propre délai maximal et ne doit être appelé qu'une fois par lot de projectiles.
Lire les messages d'erreur
Lorsqu'un pouvoir Lua échoue à l'exécution, la console affiche un bloc préfixé par [Lua], indiquant le fichier et le hook ou callback. Un numéro de ligne apparaît s'il peut être extrait de l'erreur d'origine. Lisez le message complet avant de déboguer. Avec -- @include, les lignes indiquées correspondent au code développé.
Une erreur typique ressemble à ceci :
[Lua] Error in 'push_zone.lua' at line 35 during 'on_boss_damaged_by_player':
[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. Voici les plus fréquentes :
| Erreur Lua brute | Ce que la console vous indique |
|---|---|
attempt to call nil | Vous avez tenté d'appeler une méthode ou une fonction qui n'existe pas. Vérifiez le nom de la méthode pour des fautes de frappe, ou assurez-vous d'utiliser : (deux-points) pour les appels de méthode, et non . (point). |
index expected, got nil | Vous avez tenté d'accéder à un champ sur quelque chose qui est nil. Vérifiez qu'un code antérieur l'a bien initialisé. |
attempt to index | Vous avez tenté d'accéder à une propriété sur une valeur nil ou invalide. |
bad argument | Affiche les détails spécifiques de la non-correspondance d'argument (type attendu vs type réel). |
| Budget dépassé | Le message indique la limite d'instructions, de temps CPU ou de temps écoulé de secours ; voir ci-dessus. |
L'instance du script défaillant est désactivée pour cette entité. Après correction du fichier, il faut recharger sa définition et créer un nouveau runtime ; une instance désactivée inchangée ne réessaie pas le hook défaillant à chaque tick.
Ne supposez pas que des alias non documentés existent
L'API Lua expose un ensemble spécifique de noms de méthodes. Si vous écrivez des pouvoirs à la main ou avec l'aide d'une IA, ne supposez pas que des raccourcis ou des noms alternatifs existent. Voici des exemples de noms qui n'existent pas et qui provoqueront des erreurs :
show_temporary_boss_bar()— utilisez plutôtplayer:show_boss_bar(title, color, style, duration).run_command_as_player()— utilisez plutôtplayer:run_command(command).em.location(...)— le nom de la méthode est faux. Utilisez plutôtem.create_location(x, y, z), oucontext.boss:get_location()/context.player.current_location.em.vector(...)— le nom de la méthode est faux. Utilisez plutôtem.create_vector(x, y, z), ou de simples tables{x=0, y=1, z=0}.em.zone.sphere(...)— le nom de la méthode est faux. Utilisez plutôtem.zone.create_sphere_zone(radius), ou une table de définition de zone comme{kind = "sphere", radius = 5, origin = location}.entity:teleport_to(...)— utilisezentity:teleport_to_location(location).- Avec les wrappers d'entités propres aux boss EliteMobs, utilisez
entity:set_velocity_vector(vector). Les wrappers partagés de MagmaCore exposent aussiset_velocity(...); vérifiez le wrapper renvoyé par votre contexte. entity:set_facing(...)— utilisezentity:face_direction_or_location(direction_or_location).
Consultez la référence du wrapper utilisé : Boss & Entités, Monde & Environnement, Zones & Ciblage, ou l'API partagée. Des contextes similaires peuvent exposer des méthodes différentes ; un nom d'alias plausible ne garantit pas son existence.
Conseils de migration pour les auteurs d'EliteScript
Si vous écrivez déjà de bons EliteScripts, la façon la plus simple d'apprendre les pouvoirs Lua est :
-
Continuez à raisonner en termes d'événements, de cibles, de zones, de vecteurs relatifs et de particules. Les concepts sont les mêmes — seule la syntaxe change. Les événements EliteScript deviennent des noms de hooks comme
on_spawnouon_boss_damaged_by_player. Les cibles et les zones sont passées sous forme de tables àcontext.scripten utilisant les mêmes noms de champs documentés dans les pages Zones EliteScript et Cibles EliteScript. -
Déplacez votre flux de contrôle dans Lua. Les tirages aléatoires, les fonctions d'aide partagées, les boucles, l'état persistant (
context.state) et la planification de tâches (context.scheduler) sont les choses que Lua ajoute et que l'EliteScript pur ne peut pas faire facilement. Commencez par convertir un pouvoir avec branchement ou condition en Lua tout en gardant tout le reste identique. -
Utilisez
context.scriptpour le ciblage et la géométrie des zones. Ses tables utilisent les analyseurs EliteScript pourtargetType,shape,Target,Target2,FinalTarget,range,offset,relativeOffset,coverageetfilter. Gardez la documentation EliteScript ouverte pour les noms et valeurs par défaut, mais lisez aussi les limites des objets Lua. Les objets Lua statiques n'appliquent pas la mise en cachetrack: falsede l'action YAML, et les données d'action héritées sont absentes.offsetetrelativeOffsets'appliquent aux positions résolues, y comprisLOCATION,LOCATIONSet les positions échantillonnées des zones. Utilisez une origine fixe explicite lorsqu'une zone doit rester en place. -
Attention aux deux systèmes de zones.
context.script:zone({shape = "SPHERE", ...})utilise le moteur EliteScript avec des enums en majuscules et des tablesTarget.context.zonesutilisekinden minuscules et de simples positionsorigin/destination. Mélanger ces styles de clés peut produire une zone vide ou inattendue, des avertissements ou une erreur d'exécution.
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_spawn. - Faites en sorte que le boss envoie un message ou joue un son.
- Ajoutez un délai de récupération avec
context.cooldowns. - Ajoutez un hook déclenché par le joueur tel que
on_boss_damaged_by_player. - Ajoutez une action différée avec
context.scheduler:run_after(...). - Ajoutez une requête de zone Lua native simple ou un simple
context.script:target(...). - Ce n'est qu'ensuite que vous passerez aux attaques en rotation, aux machines à états et aux mécaniques en plusieurs étapes.
Chaque étape s'appuie sur la précédente, et vous pouvez tester à chaque stade. N'essayez pas d'écrire un boss multi-phases comme premier pouvoir Lua.
Étapes suivantes
- Premiers pas — structure des fichiers, hooks, premier pouvoir pas à pas, modèles à copier-coller
- Scripts de PNJ — scripts de proximité, d'interaction et de cycle de vie des PNJ
- Exemples & Modèles — pouvoirs complets et fonctionnels que vous pouvez étudier et adapter
