Lua 脚本:故障排除
本页涵盖编写或调试 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_spawn、on_game_tick、on_enter_combat 和 on_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_location或context.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 能力最简单的方法是:
-
继续以事件、目标、区域、相对向量和粒子的概念思考。 概念相同 -- 只是语法改变了。EliteScript 事件变成了钩子名如
on_spawn或on_boss_damaged_by_player。目标和区域作为表传递给context.script,使用在 EliteScript 区域 和 EliteScript 目标 页面中记录的相同字段名。 -
将控制流移到 Lua。 随机投掷、共享辅助函数、循环、持久状态(
context.state)和任务调度(context.scheduler)是 Lua 添加的纯 EliteScript 无法轻松实现的功能。首先将一个分支或条件能力转换为 Lua,同时保持其他一切不变。 -
使用
context.script进行目标选择和区域几何。 脚本工具接受与 EliteScript 相同的字段名(targetType、shape、Target、Target2、range、offset、coverage),因此你可以继续使用现有的 EliteScript 文档作为这些规格的参考。这让你在获得 Lua 逻辑层灵活性的同时,利用熟悉的模式。
初学者学习路径
- 写一个只有
api_version = 1和on_spawn的文件。 - 让 Boss 发送消息或播放声音。
- 用
context.cooldowns添加冷却。 - 添加一个玩家触发的钩子如
on_boss_damaged_by_player。 - 用
context.scheduler:run_after(...)添加一个延迟动作。 - 添加一个简单的原生 Lua 区域查询或
context.script:target(...)。 - 然后才进入旋转攻击、状态机和多步骤机制。
