跳到主要内容

Lua 脚本:故障排除

webapp_banner.jpg

本页涵盖编写或调试 Lua 能力时可能遇到的常见问题,以及从 EliteScript 迁移的作者的迁移建议。如果你正在寻找工作示例,请参阅示例与模式。如果你刚刚起步,请参阅入门指南


常见问题

1. 能力完全无法加载

检查服务器启动时控制台的错误。最常见的原因是 Lua 语法错误(缺少 end、括号不匹配等)。还要确认文件以 .lua 结尾并放置在正确的 powers 目录中。

2. 钩子从未触发

验证钩子名称是否与钩子列表中的拼写完全一致。常见错误:on_boss_hit(错误)vs. on_boss_damaged_by_player(正确),或 on_tick(错误)vs. on_game_tick(正确)。

3. context.player 为 nil

并非所有钩子都提供玩家。on_spawnon_game_tickon_enter_combaton_exit_combat 没有玩家。在使用 context.player 之前始终添加 nil 守卫。

4. 超时 / 执行预算超限

如果钩子或回调耗时过长,能力会被自动禁用以防止卡顿。

[Lua]   -> Your script took 73ms in 'on_tick' (limit: 50ms) — power disabled to prevent lag.

5. 调度器回调使用过时数据

你可能使用了外部的 context 而不是回调参数。改为 function(tick_context) ... tick_context.boss ... end

6. 区域查询不返回任何实体

对于原生区域,kind 必须小写("sphere",不是 "SPHERE")。对于脚本工具,shape 必须大写("CONE",不是 "cone")。

7. 粒子不显示

确认粒子名称是有效的 Bukkit Particle 枚举值且为 UPPER_CASE。常见错误:"flame"(错误)vs. "FLAME"(正确)。

8. 冷却似乎不起作用

确保使用 check_local(key, duration)(一次调用中检查并设置),而不是 local_ready(key) 后跟单独的 set_local(duration, key)

9. Boss 死后继续执行能力

on_exit_combat 和/或 on_death 中添加清理逻辑以取消调度器任务。


阅读错误消息

当 Lua 能力出现问题时,控制台会打印带有 [Lua] 前缀的友好错误块。这些消息会准确告诉你哪个文件、哪一行、哪个钩子以及出了什么问题。

[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] -> Power has been disabled for this boss to prevent further errors.
原始 Lua 错误控制台告诉你的
attempt to call nil你尝试调用一个不存在的方法或函数。
index expected, got nil你尝试访问 nil 的字段。
attempt to index你尝试访问 nil 或无效值的属性。
bad argument显示具体的参数不匹配详情。
Timeout你的脚本在 'hook_name' 中耗时 Xms(限制:50ms)-- 能力已禁用。
提示

看到控制台中的 [Lua] 错误时,在深入代码之前先阅读完整消息 -- 它通常直接指向修复方法。


不要假设未文档化的别名存在

Lua API 暴露了一组特定的方法名。如果你手动编写能力或借助 AI,不要假设存在简写或替代名称。以下名称不存在,会导致错误:

  • show_temporary_boss_bar() -- 使用 player:show_boss_bar(title, color, style, duration)
  • run_command_as_player() -- 使用 player:run_command(command)
  • em.location(...) -- 没有 em 全局变量。使用 context.boss:get_location()context.player.current_locationcontext.world 方法。
  • em.vector(...) -- 没有 em 全局变量。使用 context.vectors.get_vector_between_locations(loc1, loc2) 或简单的 {x=0, y=1, z=0} 表。
  • em.zone.sphere(...) -- 没有 em 全局变量。使用区域定义表如 {kind = "sphere", radius = 5, origin = location}
  • entity:teleport_to(...) -- 使用 entity:teleport_to_location(location)
  • entity:set_velocity(...) -- 使用 entity:set_velocity_vector(vector)
  • entity:set_facing(...) -- 使用 entity:face_direction_or_location(direction_or_location)

如有疑问,请查看 API 参考页面(Boss 与实体世界与环境区域与目标选择)。如果没有文档记录,就不存在。


EliteScript 作者的迁移建议

如果你已经在编写优秀的 EliteScript,学习 Lua 能力最简单的方法是:

  1. 继续以事件、目标、区域、相对向量和粒子的概念思考。 概念相同 -- 只是语法改变了。EliteScript 事件变成了钩子名如 on_spawnon_boss_damaged_by_player。目标和区域作为表传递给 context.script,使用在 EliteScript 区域EliteScript 目标 页面中记录的相同字段名。

  2. 将控制流移到 Lua。 随机投掷、共享辅助函数、循环、持久状态(context.state)和任务调度(context.scheduler)是 Lua 添加的纯 EliteScript 无法轻松实现的功能。首先将一个分支或条件能力转换为 Lua,同时保持其他一切不变。

  3. 使用 context.script 进行目标选择和区域几何。 脚本工具接受与 EliteScript 相同的字段名(targetTypeshapeTargetTarget2rangeoffsetcoverage),因此你可以继续使用现有的 EliteScript 文档作为这些规格的参考。这让你在获得 Lua 逻辑层灵活性的同时,利用熟悉的模式。


初学者学习路径

  1. 写一个只有 api_version = 1on_spawn 的文件。
  2. 让 Boss 发送消息或播放声音。
  3. context.cooldowns 添加冷却。
  4. 添加一个玩家触发的钩子如 on_boss_damaged_by_player
  5. context.scheduler:run_after(...) 添加一个延迟动作。
  6. 添加一个简单的原生 Lua 区域查询或 context.script:target(...)
  7. 然后才进入旋转攻击、状态机和多步骤机制。

后续步骤

  • 入门指南 -- 文件结构、钩子、首个能力演练、复制粘贴模板
  • 示例与模式 -- 可供学习和改编的完整工作能力