Pular para o conteúdo principal

Scripting Lua: Solução de Problemas

Esta página cobre problemas comuns que você pode encontrar ao escrever ou depurar scripts de props do FreeMinecraftModels, além de dicas para trabalhar com o sistema de geração preguiçosa de configuração. Se você está procurando exemplos funcionais, veja Exemplos e Padrões. Se está apenas começando, veja Primeiros Passos.


Problemas Comuns

1. Arquivo de configuração não carrega / Script não vinculado

Sintoma: O prop spawna mas não responde a cliques ou quaisquer hooks.

Causas e soluções:

  • Nenhum arquivo de configuração .yml existe ainda. O FMM gera a configuração de forma preguiçosa no primeiro spawn do prop. Na primeira vez que um modelo é spawnado, o FMM cria o arquivo de configuração .yml de forma assíncrona com valores padrão (habilitado, sem scripts). Você deve editar a configuração gerada para adicionar os nomes dos seus scripts e então respawnar o prop.

  • A configuração tem isEnabled: false. Abra o arquivo .yml ao lado do arquivo de modelo e defina isEnabled: true.

  • A lista scripts: está vazia. Adicione os nomes dos seus scripts:

    isEnabled: true
    scripts:
    - my_script.lua
  • O nome do arquivo .yml não corresponde ao nome do arquivo de modelo. A configuração deve ter o mesmo nome base que o arquivo de modelo. Por exemplo, torch_01.fmmodel precisa de torch_01.yml no mesmo diretório.


2. Arquivo de script não encontrado

Sintoma: O console mostra: [FMM Scripts] Script 'my_script.lua' not found in scripts/ folder

Causas e soluções:

  • Diretório errado. Arquivos de script devem estar em plugins/FreeMinecraftModels/scripts/, não ao lado do arquivo de modelo.

  • Nome de arquivo diferente. O nome na configuração .yml deve corresponder exatamente ao nome do arquivo na pasta scripts/, incluindo maiúsculas/minúsculas e a extensão .lua.

  • Arquivo não presente. Verifique novamente se o arquivo .lua realmente existe na pasta scripts/.


3. Script não carrega de forma alguma

Sintoma: Nenhum erro no console, mas nenhum hook dispara.

Verifique o console do servidor para erros de sintaxe Lua durante a inicialização. As causas mais comuns são:

  • end faltando para fechar uma função ou bloco if
  • Parênteses não correspondentes
  • Vírgula faltando entre definições de hooks na tabela retornada
  • Arquivo não termina com .lua

Se houver um erro Lua, o console imprimirá um bloco de erro [Lua] com o nome do arquivo, número da linha e uma descrição.


4. Hook nunca dispara

Sintoma: O script carrega (você vê a mensagem [FMM Scripts] Bound script), mas um hook específico nunca é acionado.

Verifique se o nome do hook está escrito exatamente como listado na referência de hooks. Erros comuns:

Nome erradoNome correto
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

Verifique também se o prop realmente tem uma entidade armor stand de suporte. Algumas configurações de prop podem não produzir uma entidade clicável.


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

Sintoma: O console mostra uma mensagem como:

[Lua] my_script.lua took 73ms in 'on_game_tick' (limit: 50ms) -- script disabled to prevent lag.

O script estava fazendo trabalho demais em uma única chamada de hook. Causas comuns:

  • Iterando sobre muitas entidades em on_game_tick
  • Criando muitas partículas por tick
  • Executando loops caros sem distribuir o trabalho entre ticks

Solução: Mova trabalho pesado para trás de um cooldown baseado em estado ou use scheduler:run_repeating() com um intervalo razoável em vez de on_game_tick.


6. context.event é nil em um hook de clique

Isso não deveria acontecer normalmente para on_left_click e on_right_click, mas sempre proteja com:

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

Dentro de callbacks de scheduler, context.event é sempre nil -- este é o comportamento esperado. Modificação de evento só pode acontecer dentro do próprio hook de evento.


7. Animação não reproduz

Sintoma: play_animation() retorna false, ou nada visível acontece.

Causas e soluções:

  • Nome de animação errado. O nome deve corresponder exatamente ao que está definido no arquivo de modelo. Verifique seu .bbmodel ou .fmmodel para o nome correto da animação.

  • Modelo não tem animações. Nem todos os modelos têm animações. Verifique se o arquivo de modelo realmente contém dados de animação.

  • Jogadores não estão no alcance do resource pack. A animação é server-side, mas jogadores precisam do resource pack do FMM carregado para ver o modelo.


8. Partículas não aparecem

  • Verifique se o nome da partícula é um valor válido do enum Particle do Bukkit em MAIÚSCULAS: "FLAME", não "flame".
  • Verifique se a localização está em um chunk carregado. Se nenhum jogador está por perto, o chunk pode estar descarregado.
  • Verifique se count é pelo menos 1.
  • Algumas partículas (como DUST) precisam de dados extras específicos que o spawn_particle() base pode não suportar. Use partículas padrão como FLAME, HEART, HAPPY_VILLAGER, NOTE, ENCHANT, etc.

9. Som não toca

  • Verifique se o nome do som é uma constante válida do enum Sound do Bukkit em MAIÚSCULAS. Exemplo: "BLOCK_NOTE_BLOCK_HARP", não "block.note_block.harp".
  • Verifique se as coordenadas de localização estão corretas (não todos zeros ou NaN).
  • Verifique se o volume é maior que 0.

10. Estado reseta inesperadamente

context.state é por instância de prop e persiste durante a vida do prop. Se o estado parece resetar:

  • O prop pode ter sido removido e respawnado (cada spawn cria uma nova instância).
  • Você pode estar lendo estado em um callback de scheduler usando a variável de context errada. Sempre use o próprio parâmetro de context do callback.

11. Biblioteca os não está disponível

Sintoma: Script crasha com um erro como attempt to index nil (os).

A biblioteca os é completamente removida do sandbox Lua. Você não pode usar os.time(), os.clock() ou qualquer outra função os.

Solução: Use context.world:get_time() para o tempo do mundo, ou rastreie o tempo decorrido manualmente usando flags de estado e contadores de tick em on_game_tick. Para cooldowns, use flags de context.state com scheduler:run_later().


12. Script de item não ativando

Sintoma: O item customizado está na mão do jogador, mas nenhum hook de item dispara.

Causas e soluções:

  • Sem campo material: na configuração YML. Scripts de item só funcionam em modelos que têm material: definido na sua configuração. Sem este campo, o FMM trata o modelo como prop, não como item customizado.

  • Chave PDC fmm_item_id ausente. O item no inventário do jogador deve ter a chave PersistentDataContainer fmm_item_id. Itens obtidos através de comandos vanilla ou plugins de terceiros podem não ter esta tag. Use /fmm giveitem <id> ou o menu de admin para obter um item com as tags corretas.

  • Nome do arquivo de script não listado na configuração. Verifique se a configuração .yml do item tem o nome do arquivo de script na lista scripts:, assim como os props.


13. Script de item ativando e desativando imediatamente

Sintoma: Você vê o script vincular e desvincular em rápida sucessão no console, ou on_equip dispara seguido imediatamente por on_unequip.

Causa: O rastreamento de equipamento de itens dispara em eventos de mudança de slot. Se você se der um item com um comando e ele for para um slot não-ativo, ou se seu slot ativo mudar durante o give, o ciclo de equipar/desequipar pode disparar em rápida sucessão.

Solução: Depois de se dar o item com /fmm giveitem <id>, certifique-se de trocar manualmente para o slot de inventário contendo o item. O script ativa baseado em qual slot está atualmente selecionado, não apenas se o item está no inventário.


14. Hook dispara mas script crasha em métodos de entidade

Sintoma: Um hook como on_shift_right_click funciona, mas o script crasha dentro de um callback agendado com erros como attempt to call nil ao chamar entity:damage() ou entity:push().

Causa: context.world:get_nearby_entities() retorna TODAS as entidades no alcance, incluindo entidades não-vivas (armor stands, itens dropados, orbes de experiência, nuvens de efeito de área). Essas entidades não possuem métodos de entidades vivas como damage(), push() ou add_potion_effect().

Solução: Sempre proteja com if entity.damage then antes de chamar métodos de entidades vivas:

local entities = context.world:get_nearby_entities(x, y, z, radius)
for _, entity in ipairs(entities) do
if entity.damage then
-- Seguro chamar métodos de entidades vivas
entity:damage(5.0)
entity:push(0, 0.5, 0)
end
end

15. context.player é nil em callbacks agendados

Sintoma: context.player é nil dentro de um callback scheduler:run_later() ou scheduler:run_repeating(), mesmo estando disponível no hook que criou o callback.

Causa: O context é reconstruído preguiçosamente para cada invocação de callback. Em alguns casos, especialmente se o jogador desconectar ou o item for desequipado entre o agendamento e a execução, context.player pode não estar disponível.

Solução: Capture a referência do jogador de uma variável de closure antes de agendar:

on_right_click = function(context)
local player = context.player
local player_loc = player.current_location

context.scheduler:run_later(20, function()
-- Use a variável 'player' capturada, não context.player
if player.is_valid then
player:send_message("&aDelayed message!")
end
end)
end

Como a Geração Preguiçosa de Configuração Funciona

Entender o sistema de configuração preguiçosa ajuda a evitar confusão ao configurar novos props:

  1. Primeiro spawn: Quando um prop spawna e nenhum arquivo .yml irmão existe, o FMM cria a configuração de forma assíncrona. Isso significa que o arquivo é escrito em uma thread em segundo plano e não está imediatamente disponível.

  2. Valores padrão: A configuração gerada tem isEnabled: true e uma lista vazia scripts: [].

  3. Sem scripts no primeiro spawn: Como a configuração é criada depois que o prop já spawnou (e não tem scripts listados), o prop não terá scripts vinculados no seu primeiro spawn.

  4. Edite e respawne: Depois que o FMM cria a configuração, você a edita para adicionar os nomes dos seus scripts. Na próxima vez que o prop spawnar, os scripts serão carregados.

  5. Localização da configuração: O arquivo .yml é criado no mesmo diretório que o arquivo de modelo, com o mesmo nome base. Por exemplo:

    • Modelo: plugins/FreeMinecraftModels/models/fountain.fmmodel
    • Configuração: plugins/FreeMinecraftModels/models/fountain.yml
Fluxo rápido de configuração
  1. Coloque o modelo em models/
  2. Coloque o script em scripts/
  3. Spawne o prop uma vez (gera o .yml)
  4. Edite o .yml para adicionar scripts: [my_script.lua]
  5. Respawne o prop -- script agora está ativo

Lendo Mensagens de Erro

Quando algo dá errado em um script Lua de prop, o console imprime um bloco de erro [Lua]. Essas mensagens dizem exatamente qual arquivo, qual linha, qual hook e o que deu errado em linguagem simples.

Um erro típico se parece com isto:

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

O sistema traduz erros comuns de Lua para linguagem simples:

Erro Lua brutoO que o console diz
attempt to call nilVocê tentou chamar um método ou função que não existe. Verifique erros de digitação e uso de : vs ..
index expected, got nilVocê tentou acessar um campo em algo que é nil. Verifique se o código anterior o inicializou.
attempt to indexVocê tentou acessar uma propriedade em um valor nil ou inválido.
bad argumentUma função recebeu o tipo errado de argumento. A mensagem mostra o tipo esperado vs. real.
TimeoutSeu script levou Xms em 'hook_name' (limite: 50ms) -- script desabilitado para evitar lag.
dica

Sempre leia a mensagem de erro [Lua] completa antes de mergulhar no código. Ela geralmente aponta diretamente para a solução.


Não Assuma que Métodos Não Documentados Existem

A API Lua do FMM expõe um conjunto específico de métodos. Não assuma que abreviações ou nomes alternativos existem. Erros comuns:

  • context.prop:get_location() -- não existe. Use context.prop.current_location (um campo, não um método).
  • context.prop:set_animation("open") -- não existe. Use context.prop:play_animation("open", true, true).
  • context.event:setCancelled(true) -- não existe. Use context.event.cancel().
  • context.cooldowns:check_local(...) -- não existe no FMM. Use context.state com scheduler:run_later() para cooldowns.
  • context.player -- não existe em scripts de props do FMM. Use context.world:get_nearby_players() para encontrar jogadores perto do prop.
  • context.boss -- não existe. Isso é do EliteMobs. Use context.prop no FMM.

Na dúvida, consulte a página API de Props. Se não está documentado lá, não existe.


Dicas de Depuração

1. Use context.log:info() generosamente

Adicione mensagens de log em cada etapa ao depurar:

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. Verifique o console na inicialização

O FMM registra [FMM Scripts] Bound script 'X' to prop 'Y' para cada vinculação bem-sucedida. Se você não vê esta mensagem, a configuração ou script não foi carregado.

3. Verifique o caminho do arquivo de modelo

A configuração .yml deve ser irmã do arquivo de modelo (mesmo diretório, mesmo nome base). Verifique isto checando:

  • Caminho do arquivo de modelo: plugins/FreeMinecraftModels/models/my_model.fmmodel
  • Caminho do arquivo de configuração: plugins/FreeMinecraftModels/models/my_model.yml

4. Teste hooks individualmente

Ao construir um script complexo, comece testando cada hook isoladamente. Adicione um hook de cada vez e verifique se ele dispara antes de passar para o próximo.

5. Verifique erros de digitação em nomes de hooks

A razão mais comum para um hook não disparar é um nome de hook mal escrito. O script carregará sem erros, mas a função de hook mal escrita será rejeitada durante a validação.


Caminho de Progressão para Iniciantes

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

  1. Escreva um arquivo com apenas api_version = 1 e on_spawn que registra uma mensagem.
  2. Adicione o script a uma configuração de prop e verifique se a mensagem de log aparece.
  3. Adicione on_right_click e registre quando o prop é clicado.
  4. Adicione on_left_click com context.event.cancel() para invulnerabilidade.
  5. Reproduza uma animação ao clicar com botão direito.
  6. Adicione um som ao clicar com botão direito.
  7. Adicione estado e comportamento de alternância.
  8. Adicione um monitoramento de zona para detecção de proximidade.
  9. Só então passe para scripts complexos multi-hook com schedulers.

Cada passo se baseia no anterior, e você pode testar em cada etapa.


Próximos Passos

  • Primeiros Passos -- estrutura de arquivos, hooks, passo a passo do primeiro script, templates para copiar e colar
  • API de Props -- referência completa da API
  • Exemplos e Padrões -- scripts completos e funcionais que você pode estudar e adaptar