原生插件是什么

DLL 插件与脚本模组的区别、加载方式,以及作者和玩家都要清楚的风险

一句话定义

原生插件(stl-plugin)是一个 Windows x64 原生 DLL,由 stellaris-Launcher 在游戏启动后注入到 stellaris.exe 进程中运行。它可以挂钩渲染、读取游戏内存、调用引擎函数,或在游戏和外部程序之间架桥——这些都是脚本模组做不到的。

现有的例子:

插件 id做什么
stellaris-mcp让 AI 代理通过 MCP 读取游戏状态、发出游戏自己的指令
stellaris-live2d在肖像位置绘制 Live2D 模型
stellaris-perf性能优化

与脚本模组的区别

脚本模组原生插件
形态脚本、图像、本地化文件原生 DLL
谁加载游戏自己(按 dlc_load.json)stellaris-Launcher 注入
放在哪Documents\Paradox Interactive\Stellaris\mod\Documents\Paradox Interactive\Stellaris\plugins\<id>\
跨游戏版本一般跨小版本可用依赖 exe 内部地址,每次游戏更新都可能失效
能做什么游戏脚本系统开放的一切进程里能做的一切
描述文件descriptor.modstl-plugin.json(见插件清单格式)

插件和模组可以配合:例如 stellaris-live2d 的 Live2D 模型由肖像模组声明,插件负责绘制。

怎么被加载

插件只能由启动器加载。 当前的插件规范 v2(清单里 "schema": 2)不支持代理 DLL(如替身 d3dx9_43.dll)或任何放进游戏目录的加载器。从 Steam 或 Paradox 启动器直接启动的游戏不带插件,这是设计如此。

用 stl launch 或启动器的「开始游戏」启动时,启动器对当前播放集里每个启用的插件:

  1. 检查是否已安装、DLL 是否存在、清单声明的 exe 构建是否与当前游戏一致,不满足就跳过并说明原因;
  2. 补齐缺少的设置文件,启动游戏;
  3. 默认等游戏出现标题含 “stellaris” 的可见窗口,再等清单里的 delay_ms;
  4. 在游戏进程里创建远程线程执行 LoadLibraryW("<插件文件夹>\<dll>"),最多等 30 秒,再确认模块确实出现在进程里。

几个对开发者很重要的事实:

  • 插件按播放集中的顺序依次加载;等待游戏就绪的总时限默认 180 秒。
  • DllMain 运行在启动器创建的远程线程上,不是游戏主线程。
  • 窗口出现时游戏通常还在加载数据库,插件不能假设引擎已经初始化完毕。
  • 启动器从不对插件调用 FreeLibrary,插件一直留到游戏退出。

风险:作者和玩家都要知道

插件在游戏进程里拥有和游戏相同的权限,启动器无法沙箱化它。这意味着:

  • 安全:一个恶意 DLL 能做任何本地程序能做的事。只安装你信任的作者、最好是开源并公开了校验值的插件。启动器在从索引安装时会核对 SHA-256,但这只能保证“下载的就是索引里登记的那个文件”,不能保证文件本身无害。
  • 稳定:读错一个指针、在错误的线程调用引擎函数,都可能让游戏静默崩溃或卡死。规范要求原始内存读取用 SEH 保护、引擎调用只在主线程进行(见开发与调试流程)。
  • 版本:游戏更新后内部地址会变。清单里的 game.exe_timestamps 用来声明插件适配的 stellaris.exe 构建,不匹配时启动器跳过加载——这是保护,不是故障。
  • 多人与成就:插件不改游戏的校验文件,但能改变游戏行为。规范要求:影响多人游戏同步或成就的功能,必须在说明中明确告知。游戏在多人模式下是否会比对 DLL,目前没有定论。

规范对插件的行为约束

以下摘自 stellaris-Launcher 的插件开发手册(“行为准则”一节),发布前请逐条确认:

  1. 不修改游戏目录、存档或其他插件的文件夹。
  2. 不在用户不知情时联网、收集或上传数据;需要联网的功能写明用途,并默认关闭或可关闭。
  3. 不加载其他插件,不安装代理 DLL、注册表启动项等加载器。
  4. 不绕过启动器的版本检查(例如自己再注入到不兼容的构建)。
  5. 应当开源,至少公开发布页和校验值;在 homepage 写清楚功能、已知问题和适配的游戏版本。

接下来

完整规范见 stellaris-Launcher 仓库的 docs/PLUGIN_HANDBOOK.zh-CN.md。