GMod 后端

GMod 不是普通 Lua 运行环境。一个插件同时涉及服务端 include、客户端文件分发、 autorun 顺序、AddCSLuaFilelua/ 挂载命名冲突和 Workshop 打包。Lux 的 GMod 后端把这些规则从手写 loader 迁移到编译器构建计划里。

后端负责什么

luxc gmod build 会执行这些步骤:

  1. 递归发现 source_root 下的 .lux 文件。
  2. 按目录和文件名前缀推断每个 part 的默认运行域。
  3. 把同一模块目录下的 part 合并成一个逻辑模块。
  4. 解析相对导入、包导入、导出映射和运行域可用性。
  5. 按客户端/服务端生成需要的模块产物。
  6. 编译依赖到的运行时包,例如 @lux/std@lux/gmod,以及项目安装的外部包。
  7. 写出 .lua.lua.map.json、Lux loader tree 和可选 autorun forwarder。
  8. 可选地生成 gmad 打包命令。

后端不会自动发布 Workshop,也不会在没有 --run 的情况下调用 gmad

后端不会假设所有项目都必须拥有 lua/autorun。普通 init 默认生成 addon-friendly 配置;当已有 gamemode、框架或手写入口负责启动时,设置 autorun = false 即可。

最小配置

package_id = "my_addon"
bundle_id = "my_addon"

[target.gmod]
source_root = "src"
out = "generated/lua"
runtime_base = "lux/my-addon"
autorun = true
source_comments = "boundary"

[target.gmod.realm]
unknown_external = "warn"

必要字段只有 source_rootout。其余字段建议在真实项目中显式写出:

  • package_id:项目模块 ID 前缀。
  • bundle_id:生成私有注册表命名空间,省略时默认使用 package_id
  • out:物理输出根,所有产物都写在这里下面。
  • runtime_base:生成 include / AddCSLuaFile 使用的 GMod 相对路径。
  • autorun:是否额外生成 addon 风格 autorun forwarder。
  • source_comments:源码注释模式。
  • unknown_external:未知外部符号策略。

如果不写 package_id,编译器会从源码目录或其父目录推断。团队项目建议显式指定, 避免目录改名导致生成路径变化。

生成布局

package_id = "my_addon"bundle_id = "my_addon" 为例:

generated/
  lua/
    autorun/
      my_addon.lua
    lux/
      my-addon/
        loader_shared.lua
        loader_client.lua
        loader_server.lua
        client/
          my_addon/hud.lua
          runtime/lux/std.lua
        server/
          my_addon/init.lua
          runtime/lux/std.lua

autorun/my_addon.lua 只是薄入口。真正的模块注册、__lux_import、load order 和 客户端文件分发都在 runtime_base 下的 Lux loader tree 里。设置 autorun = false 只会关闭这个薄入口,不会关闭 Lux loader。

构建命令

使用配置文件:

luxc gmod build --manifest path\to\lux.toml

查看计划但不写文件:

luxc gmod build --manifest path\to\lux.toml --dry-run

临时测试时也可以直接给路径:

luxc gmod build path\to\src --out path\to\generated\lua

构建输出会报告模块数量、产物数量和三个 loader 路径:

GMod build planned 8 module(s), 12 artifact(s)
shared loader: generated\lua\lux\my-addon\loader_shared.lua
client loader: generated\lua\lux\my-addon\loader_client.lua
server loader: generated\lua\lux\my-addon\loader_server.lua
autorun forwarder: generated\lua\autorun\my_addon.lua
wrote generated Lua, source maps, loader files, and optional autorun forwarder

运行域产物

共享模块会在客户端和服务端都注册。客户端模块只进入客户端 loader,服务端模块只进入 服务端 loader。源码里可以混合运行域。

server block 只进入服务端产物
fn ENT:Initialize() {
  self:SetCustomCollisionCheck(true)

  server {
    self:PhysicsInit(SOLID_VPHYSICS)
  }
}

后端会把 server { ... } 的内容只发往服务端产物。共享部分仍然进入双方产物。

打包为 GMA

luxc gmod package `
  --manifest path\to\lux.toml `
  --root generated `
  --gmad "C:\Program Files (x86)\Steam\steamapps\common\GarrysMod\bin\gmad.exe" `
  --out dist\my_addon.gma

默认只打印打包命令计划,不执行:

package dry run: pass --run to execute gmad

确认无误后再加 --run

luxc gmod package --manifest path\to\lux.toml --root generated --gmad path\to\gmad.exe --out dist\my_addon.gma --run

开发建议

  • 先让 out 指向独立目录,确认生成内容后再复制到 live addon 或 gamemode 入口目录。
  • 保留 .lua.map.json,GMod 报错时用 map-error 查回 Lux 源码。
  • 迁移早期使用 unknown_external = "warn",整理完第三方 API 后在 CI 中改成 error
  • 不要手写生成目录里的 loader;这些文件每次构建都会被覆盖。需要自有入口时设置 autorun = false,再从入口 include 生成的 loader。