Theseus 模组文档
教程

序章:把环境搭起来

一个能编译、能进游戏、在模组列表里显示 `ACTIVE` 的空模组工程。之后所有教程都从这份工程继续。


为什么要先做这一步

写模组最常见的卡点不是代码,而是「环境不对」:JDK 版本低了、API jar 拿错版本、jar 没放进 mods\、入口类名写错。任何一个都会让游戏静默地不加载你的模组。

这一章只做一件事:把最短路径走通并验证。跑通之后再写代码,出问题就只可能是代码问题。

三个名词,先混个脸熟
  • 宿主(host):游戏本体。它负责加载模组、提供 API、在合适的时候回调你的代码。
  • mod-api:一个只含接口定义的瘦 jar(mod-api-3.2.jar)。编译时依赖它,运行时由游戏提供——不要把它打进自己的模组 jar。
  • 模组 jar:你最终产出的文件,放进游戏根目录的 mods\ 后由宿主加载。

你需要准备

东西说明怎么确认
JDK 17 或更高编译模组用命令行执行 javac -version,输出 17 以上
游戏本体提供 mods\ 目录与运行环境能正常启动游戏
mod-api-3.2.jar编译期 API来自文档站「资源」页
gson-2.10.1.jarAPI 签名里用到 JsonObject,编译期需要同上(模板已内置)
模板工程自带 build.bat 与示例代码文档站「资源」页的 mods-template.zip
为什么需要一个单独的 mod-api jar

宿主内部有成千上万个类,但模组只应该看见「被承诺过」的那一小部分。mod-api 就是这份承诺:只有接口和少量值对象,没有实现。

好处有两条:你的代码不会意外依赖宿主内部实现(宿主升级不会把你打挂);宿主能在加载时限制你能访问什么——引用内核类会被拒绝加载。

步骤

检查 JDK

打开命令行:

bat
javac -version
java -version

两条都要是 17 或更高。提示「不是内部或外部命令」说明 JDK 没装或没进 PATH,先解决这个再往下走。

解压模板,认识目录

把 mods-template.zip 解压到任意目录(路径不要带空格和中文,部分工具链对空格敏感):

text
my-mod/
├── mod-api-3.2.jar      ← 编译期 API(模板已带)
├── gson-2.10.1.jar      ← 编译期依赖(模板已带)
├── build.bat            ← 一键构建脚本
├── pom.xml              ← Maven 路线(与 build.bat 二选一)
├── README.md
└── src/main/
    ├── java/com/example/mymod/ExampleMod.java
    └── resources/assets/example_mod/

src/main/java 放代码,src/main/resources 放资源;资源在 jar 里的路径就是 assets/...。

改构建脚本里的变量

用记事本打开 build.bat,找到顶部这几行改成你自己的:

bat
set "MOD_ID=hello_mod"
set "MOD_NAME=Hello Mod"
set "MOD_VERSION=0.1.0"
set "MOD_CLASS=com.example.hellomod.HelloMod"
set "API_VERSION=3.2"

四条注意:

  • MOD_ID 只能用小写字母、数字、_、.、-;它同时是资源命名空间和数据目录名;
  • MOD_CLASS 必须是入口类的全限定名,要和 package + 类名完全一致;
  • API_VERSION 写 3.2;
  • 改完把示例的包名/类名一起改成 com.example.hellomod.HelloMod,或者反过来把 MOD_CLASS 指向示例类。
构建

双击 build.bat,或在命令行执行:

bat
build.bat

成功时产物在 target\hello_mod-0.1.0.jar。脚本实际做了四件事:javac 编译 → 复制资源 → 写入 manifest(入口类、模组 id、版本、API 版本等)→ jar 打包。

这一步的正确结果
  • 终端最后没有 error: 字样;
  • target\ 下出现 hello_mod-0.1.0.jar;
  • 用压缩软件打开 jar,能看到 META-INF/MANIFEST.MF 和你的 .class。
放进游戏并确认加载

把 jar 复制到游戏根目录的 mods\:

text
<游戏目录>/
├── mods/
│   └── hello_mod-0.1.0.jar
└── (游戏本体文件…)

重启游戏,打开模组列表找到你的模组。状态必须是 ACTIVE。

状态含义该做什么
ACTIVE加载成功并已激活继续下一步
REGISTERED已注册但没启用在列表里启用,然后重启游戏
LOAD_FAILED启用但加载失败看列表里的失败原因 + 游戏日志
为什么启用/停用要重启

注册在启动期一次性完成:宿主先让所有模组跑完 onPreInit,随即冻结注册表,之后运行期只读。这样判定与渲染热路径不用每帧检查「注册表有没有变」,代价就是改启用状态必须重启。

看日志

在游戏日志里搜模组 id(日志行前缀是 [<模组id>],logger 名为 seecmd.mod.<模组id>)。能看到入口类打印的初始化日志,就说明代码真的跑起来了。

常见失败对照

加载失败时按这张表排查
现象原因处理
LOAD_FAILED,原因含「此模组要求 API X,宿主支持 Y」API_VERSION 与宿主 major 不一致改成 3.2,重新构建
LOAD_FAILED,原因含「找不到入口类」MOD_CLASS 与实际包名/类名不一致核对 package 与 manifest
LOAD_FAILED,原因含「内核访问被拒绝」代码 import 了 com.seecmd.* 下的宿主实现类只 import com.seecmd.mod.api.*
列表里看不到模组jar 没放进 mods\,或目录层级不对确认是 <游戏目录>\mods\xxx.jar
状态 REGISTERED模组未启用启用并重启游戏
序章完成标志
  • javac -version ≥ 17
  • build.bat 无错误,产出 target\<id>-<ver>.jar
  • jar 已放入 mods\
  • 模组列表状态为 ACTIVE
  • 日志中能看到 [<模组id>] 前缀的输出

接下来

环境通了,下一步写真正的最小模组:第一个模组。