跳至正文
版本:最新版 - a1.169.2 ⚖️

包加载与 Lua 环境

包及其脚本的加载顺序、彼此共享代码的方式,以及 Lua 环境中的可用功能。

加载顺序​

服务器启动时,会按以下顺序加载 Config.toml 的 [game] 节中列出的包:

  1. 游戏模式包(game_mode)
  2. 脚本包(packages),按其列出的顺序
  3. 地图包(map)

对于每个包,在运行其任何脚本之前,服务器首先会加载其 assets_requirements 和 packages_requirements(并递归加载它们自身的依赖项)。随后脚本按以下顺序运行:

  1. Shared/Index.lua
  2. Server/Index.lua(服务器)或 Client/Index.lua(客户端)
  3. 触发包 Load 事件

客户端在下载完其 Client/ 和 Shared/ 文件后,会按相同的顺序加载相同的包。

提示

查阅服务器与客户端生命周期页面,获取服务器启动和客户端连接时所发生的一切事件的完整图表。

包需求​

如果你的包使用了由另一个包定义的内容(例如 default-weapons 中的武器类,或者某个库导出的函数),请将其添加到 Package.toml 的 packages_requirements 中。这能确保它在你的脚本运行之前完成加载:

Package.toml
[script]
# ...
packages_requirements = [
"default-weapons",
]
信息

只有 script(以及 c-module)类型的包可以作为需求项被引入。如果未安装所需的包,当服务器带 --auto_download 启动时,可以自动从资源库下载它。

避免在运行时使用 Server.LoadPackage() 加载依赖项,因为你的代码可能会在其准备就绪之前运行。

Load 与 Unload 事件​

Server/Index.lua
-- 在该包的所有 Index.lua 文件运行完毕后触发
Package.Subscribe("Load", function()
Console.Log("我的包已加载!")
end)

-- 当该包被卸载时触发(服务器停止、地图更改或包重新加载)
Package.Subscribe("Unload", function()
-- 在此处保存你的数据,例如使用 Package.SetPersistentData
end)

如果 Package.toml 中启用了 auto_cleanup(默认已启用),则该包生成的所有实体在卸载时都会被销毁。

将代码拆分到多个文件中​

只有每个文件夹中的 Index.lua 会自动运行。若要加载其他文件,请使用 Package.Require():

Server/Index.lua
Package.Require("Weapons.lua") -- my-package/Server/Weapons.lua
Package.Require("Utils/Math.lua") -- my-package/Server/Utils/Math.lua

文件按以下顺序查找,因此你通常只需要指定文件名:

  1. 相对于当前文件
  2. 相对于 my-package/Server/ 或 my-package/Client/(取决于端)
  3. 相对于 my-package/Shared/
  4. 相对于 my-package/
  5. 相对于 Packages/

文件仅会执行一次,再次引入它将返回缓存的结果。

在包之间共享代码​

每个包均在其自身环境中运行:你在一个包中定义的全局变量和函数对其他包是不可见的。这可以避免使用相同名称的包之间产生冲突。

若要使某些内容对所有包可用,请使用 Package.Export() 将其导出:

my-library/Server/Index.lua
MyLibrary = {}

function MyLibrary.Greet(player)
Chat.SendMessage(player, "Hello " .. player:GetName())
end

Package.Export("MyLibrary", MyLibrary)
my-game-mode/Server/Index.lua
-- 只要 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。