原生插件是什么
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.mod | stl-plugin.json(见插件清单格式) |
插件和模组可以配合:例如 stellaris-live2d 的 Live2D 模型由肖像模组声明,插件负责绘制。
怎么被加载
插件只能由启动器加载。 当前的插件规范 v2(清单里 "schema": 2)不支持代理 DLL(如替身 d3dx9_43.dll)或任何放进游戏目录的加载器。从 Steam 或 Paradox 启动器直接启动的游戏不带插件,这是设计如此。
用 stl launch 或启动器的「开始游戏」启动时,启动器对当前播放集里每个启用的插件:
- 检查是否已安装、DLL 是否存在、清单声明的 exe 构建是否与当前游戏一致,不满足就跳过并说明原因;
- 补齐缺少的设置文件,启动游戏;
- 默认等游戏出现标题含 “stellaris” 的可见窗口,再等清单里的
delay_ms; - 在游戏进程里创建远程线程执行
LoadLibraryW("<插件文件夹>\<dll>"),最多等 30 秒,再确认模块确实出现在进程里。
几个对开发者很重要的事实:
- 插件按播放集中的顺序依次加载;等待游戏就绪的总时限默认 180 秒。
DllMain运行在启动器创建的远程线程上,不是游戏主线程。- 窗口出现时游戏通常还在加载数据库,插件不能假设引擎已经初始化完毕。
- 启动器从不对插件调用
FreeLibrary,插件一直留到游戏退出。
风险:作者和玩家都要知道
插件在游戏进程里拥有和游戏相同的权限,启动器无法沙箱化它。这意味着:
- 安全:一个恶意 DLL 能做任何本地程序能做的事。只安装你信任的作者、最好是开源并公开了校验值的插件。启动器在从索引安装时会核对 SHA-256,但这只能保证“下载的就是索引里登记的那个文件”,不能保证文件本身无害。
- 稳定:读错一个指针、在错误的线程调用引擎函数,都可能让游戏静默崩溃或卡死。规范要求原始内存读取用 SEH 保护、引擎调用只在主线程进行(见开发与调试流程)。
- 版本:游戏更新后内部地址会变。清单里的
game.exe_timestamps用来声明插件适配的stellaris.exe构建,不匹配时启动器跳过加载——这是保护,不是故障。 - 多人与成就:插件不改游戏的校验文件,但能改变游戏行为。规范要求:影响多人游戏同步或成就的功能,必须在说明中明确告知。游戏在多人模式下是否会比对 DLL,目前没有定论。
规范对插件的行为约束
以下摘自 stellaris-Launcher 的插件开发手册(“行为准则”一节),发布前请逐条确认:
- 不修改游戏目录、存档或其他插件的文件夹。
- 不在用户不知情时联网、收集或上传数据;需要联网的功能写明用途,并默认关闭或可关闭。
- 不加载其他插件,不安装代理 DLL、注册表启动项等加载器。
- 不绕过启动器的版本检查(例如自己再注入到不兼容的构建)。
- 应当开源,至少公开发布页和校验值;在
homepage写清楚功能、已知问题和适配的游戏版本。
接下来
完整规范见 stellaris-Launcher 仓库的 docs/PLUGIN_HANDBOOK.zh-CN.md。