Pular para o conteúdo principal

Scripting Lua: Resolução de Problemas

webapp_banner.jpg

Esta página aborda problemas comuns que pode encontrar ao escrever ou depurar poderes em Lua, além de conselhos de migração para autores que vêm do EliteScript. Se está a depurar scripts de NPC, veja Scripts de NPC. Se procura exemplos funcionais, veja Exemplos e Padrões. Se está apenas a começar, veja Começar.

Motor Lua Partilhado

O EliteMobs usa o motor de scripting Lua do MagmaCore, partilhado entre os plugins da Nightbreak. Para documentação sobre conceitos partilhados como a sandbox, o agendador (scheduler), as zonas, a API de mundo, as tabelas de entidades e os métodos de UI do jogador, veja a página Motor de Scripting Lua do MagmaCore.


Problemas Comuns

1. O poder não carrega de todo

Verifique a consola do servidor à procura de erros quando o servidor arranca. A causa mais comum é um erro de sintaxe Lua (falta de end, parênteses por fechar, etc.). Verifique também que o ficheiro termina em .lua e está colocado no diretório powers correto.

2. O hook nunca dispara

Verifique que o nome do hook está escrito exatamente como listado na lista de hooks. Erros comuns: on_boss_hit (errado) vs. on_boss_damaged_by_player (correto), ou on_tick (errado) vs. on_game_tick (correto).

3. context.player é nil

Nem todos os hooks fornecem um jogador. on_spawn, on_game_tick e on_exit_combat não têm um jogador.

Ele é preenchido em on_boss_damaged_by_player, on_player_damaged_by_boss, on_enter_combat e on_boss_target_changed, e em on_zone_enter / on_zone_leave quando a entidade envolvida por acaso é um jogador. Adicione sempre uma proteção contra nil antes de o usar. Ver Hooks e Ciclo de Vida para a tabela completa.

4. Timeout / orçamento de execução excedido

Cada hook, callback agendado e avaliação de ficheiro partilha com as chamadas aninhadas um orçamento de 250 000 instruções Lua e 50 ms de tempo de CPU da thread. Se a JVM não conseguir medir esse tempo de CPU, aplica-se um limite alternativo de 250 ms de tempo decorrido. O erro identifica o limite excedido:

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)

A VM interrompe ciclos Lua descontrolados durante a execução. Não consegue interromper uma chamada Java em curso, e não existe um segundo limite de 50 ms após o retorno. Use cooldowns, menos pontos de amostragem ou context.scheduler:run_every(...) para distribuir o trabalho por vários ticks.

5. O callback do agendador usa dados desatualizados

Provavelmente está a usar o context exterior em vez do parâmetro do callback. Mude function() ... context.boss ... end para function(tick_context) ... tick_context.boss ... end.

6. A consulta de zona não devolve entidades

Verifique novamente a definição da zona. Para zonas nativas, garanta que kind está em minúsculas ("sphere", não "SPHERE"). Para utilitários de script, garanta que shape está em maiúsculas ("CONE", não "cone"). Verifique também que origin ou Target realmente resolve para uma localização válida.

7. As partículas não aparecem

Use um nome válido do enum Particle do Bukkit; "FLAME" e "flame" são normalizados para maiúsculas. Verifique a localização, a quantidade e os dados necessários. BLOCK exige dados de bloco que este auxiliar do mundo não fornece: use outra partícula suportada ou a API de partículas de script com a opção de material.

8. O cooldown parece não funcionar

Certifique-se de que está a usar check_local(key, duration) (que verifica E define numa única chamada), e não local_ready(key) seguido de um set_local(duration, key) separado. Se usar local_ready sozinho, apenas verifica mas nunca define o cooldown.

9. O boss continua a usar o poder depois de morrer

Adicione lógica de limpeza em on_exit_combat e/ou on_death para cancelar as tarefas do agendador. Se o boss morrer, o on_exit_combat deverá disparar, mas adicionar limpeza explícita em ambos os hooks é mais seguro.


Ler as Mensagens de Erro

Quando algo corre mal num poder Lua, a consola imprime um bloco de erro amigável prefixado com [Lua]. Estas mensagens dizem-lhe exatamente qual o ficheiro, qual a linha, qual o hook e o que correu mal -- em linguagem simples. Leia sempre a mensagem completa antes de depurar.

Um erro típico tem este aspeto:

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

O sistema traduz os erros comuns de Lua para linguagem simples. Eis os mais comuns:

Erro Lua brutoO que a consola lhe diz
attempt to call nilTentou chamar um método ou função que não existe. Verifique se há erros de escrita no nome do método, ou certifique-se de que está a usar : (dois-pontos) para chamadas de método, não . (ponto).
index expected, got nilTentou aceder a um campo de algo que é nil. Verifique se código anterior o inicializou.
attempt to indexTentou aceder a uma propriedade de um valor nil ou inválido.
bad argumentMostra os detalhes específicos da incompatibilidade do argumento (tipo esperado vs. tipo real).
Orçamento excedidoA mensagem identifica o limite de instruções, tempo de CPU ou tempo decorrido alternativo; consulte a secção acima.
dica

Quando vir um erro [Lua] na consola, a mensagem de erro diz-lhe exatamente qual o ficheiro, qual a linha, qual o hook e o que correu mal em linguagem simples. Leia a mensagem completa antes de mergulhar no código -- geralmente aponta-lhe diretamente para a solução.


Não Assuma que Existem Aliases Não Documentados

A API Lua expõe um conjunto específico de nomes de métodos. Se está a escrever poderes à mão ou com assistência de IA, não assuma que existem nomes abreviados ou alternativos. Os seguintes são exemplos de nomes que não existem e que irão causar erros:

  • show_temporary_boss_bar() -- use player:show_boss_bar(title, color, style, duration) em vez disso.
  • run_command_as_player() -- use player:run_command(command) em vez disso.
  • em.location(...) -- o nome do método está errado. Use em.create_location(x, y, z) em vez disso, ou context.boss:get_location() / context.player.current_location.
  • em.vector(...) -- o nome do método está errado. Use em.create_vector(x, y, z) em vez disso, ou tabelas {x=0, y=1, z=0} simples.
  • em.zone.sphere(...) -- o nome do método está errado. Use em.zone.create_sphere_zone(radius) em vez disso, ou uma tabela de definição de zona como {kind = "sphere", radius = 5, origin = location}.
  • entity:teleport_to(...) -- use entity:teleport_to_location(location).
  • entity:set_velocity(...) -- use entity:set_velocity_vector(vector).
  • entity:set_facing(...) -- use entity:face_direction_or_location(direction_or_location).

Na dúvida, consulte as páginas da Referência da API (Bosses e Entidades, Mundo e Ambiente, Zonas e Alvos). Se não estiver documentado lá, não existe.


Conselhos de Migração Para Autores de EliteScript

Se já escreve bons EliteScripts, a forma mais fácil de aprender os poderes em Lua é:

  1. Continue a pensar em termos de eventos, alvos, zonas, vetores relativos e partículas. Os conceitos são os mesmos -- só a sintaxe muda. Os eventos do EliteScript tornam-se nomes de hook como on_spawn ou on_boss_damaged_by_player. Os alvos e zonas são passados como tabelas para context.script usando os mesmos nomes de campo documentados nas páginas Zonas do EliteScript e Alvos do EliteScript.

  2. Mova o seu fluxo de controlo para Lua. Sorteios aleatórios, funções auxiliares partilhadas, ciclos, estado persistente (context.state) e agendamento de tarefas (context.scheduler) são as coisas que o Lua acrescenta e que o EliteScript puro não consegue fazer facilmente. Comece por converter um poder com ramificações ou condições para Lua, mantendo tudo o resto igual.

  3. Use context.script para a geometria de alvos e zonas. Os utilitários de script aceitam os mesmos nomes de campo que o EliteScript (targetType, shape, Target, Target2, range, offset, coverage), por isso pode continuar a usar a documentação existente do EliteScript como referência para essas especificações. Isto permite-lhe aproveitar padrões familiares enquanto ganha a flexibilidade do Lua para a camada da lógica.


Caminho de Progressão para Iniciantes

Se quer aprender este sistema do zero, esta progressão funciona bem:

  1. Escreva um ficheiro apenas com api_version = 1 e on_spawn.
  2. Faça o boss enviar uma mensagem ou tocar um som.
  3. Adicione um cooldown com context.cooldowns.
  4. Adicione um hook acionado pelo jogador, como on_boss_damaged_by_player.
  5. Adicione uma ação atrasada com context.scheduler:run_after(...).
  6. Adicione uma consulta de zona Lua nativa simples ou um context.script:target(...) simples.
  7. Só então avance para ataques rotativos, máquinas de estados e mecânicas de múltiplos passos.

Cada passo constrói sobre o anterior, e pode testar em todas as fases. Não tente escrever um boss de múltiplas fases como o seu primeiro poder Lua.


Próximos Passos

  • Começar -- estrutura de ficheiros, hooks, primeiro poder passo a passo, modelos para copiar e colar
  • Scripts de NPC -- scripts de proximidade, interação e ciclo de vida de NPCs
  • Exemplos e Padrões -- poderes funcionais completos que pode estudar e adaptar