包加载与 Lua 环境
包及其脚本的加载顺序、彼此共享代码的方式,以及 Lua 环境中的可用功能。
加载顺序
服务器启动时,会按以下顺序加载 Config.toml 的 [game] 节中列出的包:
- 游戏模式包(
game_mode) - 脚本包(
packages),按其列出的顺序 - 地图包(
map)
对于每个包,在运行其任何脚本之前,服务器首先会加载其 assets_requirements 和 packages_requirements(并递归加载它们自身的依赖项)。随后脚本按以下顺序运行:
Shared/Index.luaServer/Index.lua(服务器)或Client/Index.lua(客户端)- 触发包
Load事件
客户端在下载完其 Client/ 和 Shared/ 文件后,会按相同的顺序加载相同的包。
查阅服务器与客户端生命周期页面,获取服务器启动和客户端连接时所发生的一切事件的完整图表。
包需求
如果你的包使用了由另一个包定义的内容(例如 default-weapons 中的武器类,或者某个库导出的函数),请将其添加到 Package.toml 的 packages_requirements 中。这能确保它在你的脚本运行之前完成加载:
[script]
# ...
packages_requirements = [
"default-weapons",
]
只有 script(以及 c-module)类型的包可以作为需求项被引入。如果未安装所需的包,当服务器带 --auto_download 启动时,可以自动从资源库下载它。
避免在运行时使用 Server.LoadPackage() 加载依赖项,因为你的代码可能会在其准备就绪之前运行。
Load 与 Unload 事件
-- 在该包的所有 Index.lua 文件运行完毕后触发
Package.Subscribe("Load", function()
Console.Log("我的包已加载!")
end)
-- 当该包被卸载时触发(服务器停止、地图更改或包重新加载)
Package.Subscribe("Unload", function()
-- 在此处保存你的数据,例如使用 Package.SetPersistentData
end)
如果 Package.toml 中启用了 auto_cleanup(默认已启用),则该包生成的所有实体在卸载时都会被销毁。
将代码拆分到多个文件中
只有每个文件夹中的 Index.lua 会自动运行。若要加载其他文件,请使用 Package.Require():
Package.Require("Weapons.lua") -- my-package/Server/Weapons.lua
Package.Require("Utils/Math.lua") -- my-package/Server/Utils/Math.lua
文件按以下顺序查找,因此你通常只需要指定文件名:
- 相对于当前文件
- 相对于
my-package/Server/或my-package/Client/(取决于端) - 相对于
my-package/Shared/ - 相对于
my-package/ - 相对于
Packages/
文件仅会执行一次,再次引入它将返回缓存的结果。
在包之间共享代码
每个包均在其自身环境中运行:你在一个包中定义的全局变量和函数对其他包是不可见的。这可以避免使用相同名称的包之间产生冲突。
若要使某些内容对所有包可用,请使用 Package.Export() 将其导出:
MyLibrary = {}
function MyLibrary.Greet(player)
Chat.SendMessage(player, "Hello " .. player:GetName())
end
Package.Export("MyLibrary", MyLibrary)
-- 只要 my-library 在此前已加载(例如在 packages_requirements 中配置)即可正常运行
Player.Subscribe("Ready", function(player)
MyLibrary.Greet(player)
end)
通过 Inherit 创建的类(例如 MyProp = Prop.Inherit("MyProp"))会被全局注册,因此也可以从同一端侧的任何包进行访问。
另一种通信方式是使用事件,请参阅在包之间进行通信。
Lua 环境
nanos world 运行 Lua 5.4,可以使用其绝大部分标准库,以及 nanos world API(类、静态类、结构体和工具库)。
出于安全考虑,某些函数默认被禁用:
- 访问操作系统的函数:
os.execute、os.rename、os.remove、os.exit、os.getenv、os.tmpname、os.setlocale、dofile、loadfile以及整个io库。 debug库,但debug.traceback、debug.getinfo、debug.sethook和debug.gethook除外,因为其他函数可以读写其他函数的上值和局部变量、注册表以及元表。- 加载预编译的 Lua 字节码,通过
load或作为包文件。只能加载文本块。
在服务器上,可以通过 --enable_unsafe_libs 参数启用它们,但只有在你信任服务器上运行的所有包时才这样做。在客户端,它们始终被禁用,因为玩家会加入他们无法信任的服务器。
为了帮助你调试自己的脚本,可以在客户端的设置 > 调试中启用整个 debug 库。此设置不会被保存:它仅在关闭游戏前有效,并在你加入服务器(或包重新加载)时生效。上述其他函数在客户端上保持禁用状态。仅在您自己的服务器上运行时启用此功能,因为它允许服务器脚本读取和更改任何其他脚本的内部代码。
若要读取和写入文件,请改用 File 类,该类在服务器文件夹范围内生效。若要存储数据,推荐优先使用持久化数据或 Database。