生成 Loader

传统 GLua 插件经常需要手写 includeAddCSLuaFile 和客户端/服务端分支。Lux 不要求 开发者维护这种加载顺序。编译器已经知道模块图、运行域和生成路径,因此可以生成确定 性的 Lux loader tree。autorun 只是可选的薄入口。

手写 loader 被生成 loader 替代
if SERVER then AddCSLuaFile("cl_panel.lua") end
include("shared.lua")
if SERVER then include("sv_store.lua") end
if CLIENT then include("cl_panel.lua") end

Loader 文件

当前 GMod 后端在 runtime_base 下生成三个 loader:

lux/my-addon/loader_shared.lua
lux/my-addon/loader_client.lua
lux/my-addon/loader_server.lua

autorun = true 时,Lux 额外生成一个 addon 风格的 forwarder:

autorun/my_addon.lua

设置 autorun = false 不会关闭 Lux loader,只是不生成 autorun 入口。已有 gamemode、框架或手写入口可以自己 include 这些 loader。

bundle_id 用于生成 Lua 模块根目录和私有注册表名。通常让 package_idbundle_id 相同即可;需要在同一插件里打多个 bundle 时再区分它们。

AddCSLuaFile 批处理

生成的 shared loader 会把相邻的客户端发送操作合并到一个 if SERVER 块。

AddCSLuaFile 批处理
if SERVER then AddCSLuaFile("lux/my-addon/loader_shared.lua") end
if SERVER then AddCSLuaFile("lux/my-addon/loader_client.lua") end
if SERVER then AddCSLuaFile("lux/my-addon/client/runtime/lux/std.lua") end
if SERVER then AddCSLuaFile("lux/my-addon/client/my_addon/hud.lua") end

GMod 仍然看到标准 AddCSLuaFile,只是由编译器生成。

模块工厂

每个 Lux 模块会被包装成一个 Lua 工厂。

模块工厂
-- module-private bindings
-- export { publicApi = helper }

loader include 这个文件后,把工厂执行结果注册进当前 bundle 的私有表。

私有注册表

loader 开头会生成 bundle 私有注册表。这个注册表是生成代码内部细节。手写 GLua 不 应该读取或修改它。跨模块访问应通过 Lux importexport 完成。

私有注册表
-- Lux import/export 会被编译器接到 bundle 私有注册表。
-- 用户代码不直接读取这个表。

注册模块

loader 注册模块的形状如下。真实模块 ID 由编译器生成,可能包含运行域后缀。开发者 不需要记住这些 ID。

注册模块
-- include 模块工厂
-- 执行工厂
-- 写入 bundle 私有注册表

加载顺序

编译器按模块图做拓扑排序:

  1. 运行时包先于依赖它的项目模块。
  2. 被导入模块先于导入者。
  3. 共享模块在客户端和服务端 loader 中都注册。
  4. 发现循环依赖时,构建阶段报错,不输出不确定 loader。

模块内部的 part 顺序由 part order 和默认稳定排序决定。loader 只处理模块之间的 顺序,不处理 part 内部细节。

不要编辑生成文件

所有生成文件开头都有标记:

生成文件标记
-- 修改 Lux 源码、part order、导入导出或 lux.toml。
-- 不要直接编辑生成 loader。

下一次构建会覆盖生成 loader。