调试与日志
如何查找并解决脚本中的问题:读取日志与错误、快速重新加载代码以及检查 UI。
打印到控制台
Console 静态类以不同的日志级别将消息输出到控制台(及日志文件):
Console.Log("Normal message") -- 始终显示
Console.Warn("Something looks wrong") -- 橙色,显示堆栈追踪
Console.Error("Something is broken") -- 红色,显示堆栈追踪
Console.Debug("Only shown with log level 2 or 3") -- 默认隐藏
它们都支持使用 string.format 说明符进行格式化,因此你无需手动拼接值:
Console.Log("玩家 %s 拥有 %d 生命值,位于 %s", player:GetName(), character:GetHealth(), tostring(character:GetLocation()))
print(...) 同样有效,它会被重定向到 Console.Log。
若要打印表的内容,请使用 NanosTable.Dump(my_table)。
日志级别
默认情况下,Console.Debug 消息和内部详细日志是隐藏的。若要查看它们:
- 服务器:在 Config.toml 的
[debug]节中设置log_level = 2(调试)或3(详细),在启动服务器时传递--log_level 2,或运行控制台命令log_level 2。 - 客户端:在游戏设置的调试部分中更改日志级别。
日志在哪?
打印到控制台的所有内容也会保存到磁盘上:
| 端 | 位置 |
|---|---|
| 服务器 | 服务器可执行文件旁边的 .logs/ 文件夹。 NanosWorldCore.log 为最近一次会话,更早的会话将在文件名中包含日期并予以保留 |
| 客户端 | %LocalAppData%\NanosWorld\Saved\Logs\ |
在客户端上,你还可以通过按键绑定(可在设置中配置)打开游戏内控制台,以便在游玩时查看日志并运行命令。
读取 Lua 错误
当脚本运行失败时,错误会以红色打印,并指出发生位置(如在哪个事件内部),随后是堆栈追踪:导致错误的函数调用链,以及每个调用的文件和行号。第一行([1])即为错误发生的位置。例如:
ERROR Lua Error on Class 'my-package' Event 'TakeDamage':
- [1] [my-package/Server/Index.lua]:24: attempt to call a nil value (global 'Sound')
- [2] [my-package/Server/Index.lua]:77: in function <[string "my-package/Server/Index.lua"]:69>
这意味着在 my-package/Server/Index.lua 的第 24 行中,在 TakeDamage 事件内部,代码尝试调用 Sound(...),但 Sound 在此处不存在。在这种情况下,是因为 Sound 是一个仅限客户端的类,而代码正运行在服务器端!
请始终优先修复你看到的第一个错误。后续的其他错误通常都是由它引起的。
常见错误及其通常含义
attempt to index a nil value
函数返回了
nil而你直接使用了该结果,例如玩家未附身任何物体时的player:GetControlledCharacter(),或道具数量较少时的Prop.GetByIndex(10)。在使用前先检查是否为nil
attempt to call a nil value (method 'X') 或 (global 'X')
方法或类不存在,或者在对应端不可用(例如在服务器端生成仅限客户端的类)。请在脚本参考中检查其名称和主控
attempt to concatenate a boolean/nil value
对非字符串或非数字的内容使用了
..。请使用tostring(value)或Console.Log("%s", value)格式化
attempt to perform arithmetic on a nil value
用于数学计算的变量从未被赋值,例如
GetValue返回了nil。请传入回退值:GetValue("score", 0)
Expected a class as parameter
你向
IsA传入了字符串,请直接使用类本身:entity:IsA(Weapon)
Invalid Channel!、Invalid Index! 等
参数超出了允许的范围,请检查脚本参考中的方法描述
重新加载代码
测试更改时无需重启服务器。在服务器控制台中,请使用:
package reload my-package
卸载并重新加载包,销毁其生成的实体(如果已启用
auto_cleanup)。玩家将自动接收更新后的客户端文件
package hotreload my-package
重新加载所有文件,但保持 Lua 内存(全局变量)原样不变 |
package reload all
重新加载所有包,重启整个 Lua 虚拟机 |
由于重新加载时会再次触发 Load 事件,因此良好的作法是编写好你的包,使其在玩家已连接时重新加载也能正常工作。例如,在 Player "Spawn" 时为玩家生成角色,同时也应在 Package "Load" 时遍历已经连接的玩家(参阅快速开始)。
调试 WebUI
WebUI 运行在真实的 Chromium 浏览器中,因此你可以使用其开发者工具来检查 HTML、查看 JavaScript 错误并调试代码:
local my_ui = WebUI("My UI", "file://UI/index.html")
-- 打开此 WebUI 的 Chromium DevTools
my_ui:OpenDevTools()
JavaScript 的 console.log 消息也会显示在游戏控制台中,其日志类型为 WebUI。
调试世界
- Debug 静态类可在世界中绘制线段、框体、球体和文本,非常适合用于可视化位置、方向和追踪射线。追踪射线也可以使用
TraceMode.DrawDebug自行进行绘制。 - 若要查看实体的同步方式以及谁是其网络主控,请在设置中启用绘制网络调试(设置 -> 调试)。参阅调试网络与网络主控。
-- 在角色所在位置绘制一个红色球体,持续 10 秒
Debug.DrawSphere(my_character:GetLocation(), 50, 12, Color.RED, 10)
性能问题
如果服务器记录了 Server Tick too high! 警告或游戏变慢,请参阅性能分析指南以找出耗时过长的代码。