API 参考
com.seecmd.mod.api 全部接口的分类导航、稳定性策略与迁移对照(唯一权威签名见各类型页)
# Theseus 模组 API 参考(mod-api 3.2)
适用版本:mod-api 3.2(随游戏当前版本发布) 文档语言:中文 对应包:
com.seecmd.mod.api及其子包registry/spi/event/render/resource/language/capability/context/scene/audio/text/judge/collect
文档定位:本页是 API 总览(版本策略、迁移对照、常见报错);每个类型的详细参考(签名 / 行为 / 示例)各成一页,按分类挂在左侧目录的「API 参考」下。全部接口均已落地并接入宿主核心循环;文档签名与源码逐字一致,并经签名快照(
verify/api-surface.txt)机械校验,出现分歧以源码与快照为准,文档随之修正。 上手路径与完整示例见 模组开发指南;从 0.7.x 旧 API 迁移的逐项对照表随源码仓库提供(1.0 迁移指南),未在文档站发布。
怎么读这份文档
- 只写模组:从「开发指南」的入门页开始,再按需要跳到「API 参考」的各分类。
- 查类型:左侧目录「API 参考」按包分类列出全部类型页;每页给「用途 / 方法速览 / 逐方法签名与行为 / 示例」。
- 查报错:见下文「常见报错与排查」。
- 稳定性:
com.seecmd.mod.api.*的公共签名在当前 major 线内只增不破,详见「稳定性与版本策略」。
分类导航
| 分类 | 代表类型 | 什么时候看 |
|---|---|---|
| 入口与生命周期 | Mod、ModContext、ModInfo | 模组入口、阶段职责、能拿到什么 |
| registry 包 | ModRegistries、Registry、RegistryKey | 注册自有内容(音符类型、模式、判定等级…) |
| spi 包:音符与判定 | NoteTypeDefinition、NoteData | 自定义音符类型 |
| spi 包:模式与内容 | GameModeDefinition、ExtraContentApi、JudgmentKindDefinition | 自定义模式、判定等级、谱面内容原语 |
| collect 包 | CollectibleDefinition | 收集品 |
| 判定管线 | JudgmentPolicy、JudgmentAdjuster、TrackPose 与 CameraPose | 判定窗口与结果改写、位姿 |
| 事件系统 | EventBus、事件总表 | 订阅游戏事件 |
| 渲染、资源与语言 | RenderContext、RenderCanvas、ResourceManager | 绘制、贴图、译文 |
| 打击特效样式 | HitEffectStyle、HitEffectStyles | 自定义打击特效 |
| 能力接口 | CapabilityRegistry、GameStateAccess、GameplayHud、SceneHost | 接管或扩展宿主能力 |
| 只读视图与文本 | GameplayContext、ChartView、TextField | 读游玩状态、文本输入 |
包结构与依赖边界
包布局
| 包 | 内容 | 谁用 |
|---|---|---|
com.seecmd.mod.api | Mod / GameplayMod / ModInfo / ModContext / ModConfig / Notifier / Panels / PanelHandle / ModLoadStatus / LoadStage / ModLoadOrder / MissingDependencyException / ModApiVersion | 所有模组 |
api.registry | ModRegistries / Registry / RegistryKey / ResourceLocation / RegistryFrozenException | 注册自有内容 |
api.spi | 供给侧定义骨架:NoteTypeDefinition / NoteData / GameModeDefinition / JudgmentKindDefinition / ChartExtensionParser 及其上下文 / ExtraContentApi(谱面特效表演原语通道) | 自定义音符类型、模式、判定等级、谱面扩展段、谱面特效触发 |
api.event | Event / EventBus / Subscription / EventPriority / EventResult / CancellableEvent 与全部事件类(含 gameplay / input / render / resource 子包) | 订阅事件 |
api.render | RenderContext / RenderCanvas / RenderImage / RenderLayer / MatrixStack / AdvancedRenderAccess / RenderFilter / FilterFrame / BackgroundRenderer / HitEffect* / TextStyle / TextMetrics | 绘制与视觉扩展 |
api.resource | ResourceManager / ResourceNotFoundException | 读自带图片、文本、音频 |
api.language | LanguageRegistry | 贡献译文 |
api.capability | GameplayHud / GameStateAccess / SceneHost / AudioAccess / InputAccess / StoryAccess / ModManagerAccess / UiCustomization / SettingsExtension / SettingsItem / NetworkAccess / ModService / ModServiceRegistry / LaneInputMapper / GlobalHotkeys / GlobalHotkey / CapabilityRegistry | 接管或扩展宿主能力 |
api.context | 只读视图:GameplayContext / HudContext / HudValues / ChartView / NoteView / LaneView / ResultSnapshot / ResultLayout | 读游戏状态 |
api.scene | Scene / SceneInfo / InputEvent / InputType | 模组自定义场景 |
api.audio | SoundCategory / PlayingSound | 音频播放 |
api.text | TextField / ImeAnchor / ClipboardAccess | 文本输入与剪贴板 |
api.judge | JudgmentPolicy / JudgmentAdjuster / JudgeResult / JudgeResults | 判定扩展 |
api.collect | CollectibleDefinition | 收集品 |
依赖边界(强制)
模组编译期只依赖 com.seecmd:mod-api 这一个构件(对应发布物 mod-api-3.2.jar,版本随宿主演进)。该构件的边界由 verify/ApiBoundaryCheck.java 在每次校验中强制:
- API 源码不得引用
javafx.*/org.lwjgl.*/org.joml.*/javax.imageio.*等实现导向类型; java.awt只放行纯值对象java.awt.Color;com.seecmd命名空间下只允许 API 自己(不允许把内核类型泄漏进对外 API)。
唯一的例外是 gson:NoteTypeDefinition#deserialize 等契约以 com.google.gson.JsonObject 暴露,模组侧以 provided 引入(运行期由宿主提供)。模板工程的 build.bat 会自动定位 gson-*.jar(同目录优先,其次本地 Maven 仓库)。
稳定性与版本策略
3.x 内的承诺
com.seecmd.mod.api.* 的公共签名自 3.0 起冻结:
- 只允许新增:新接口、新
default方法、新注册表、新事件、新枚举常量; - 禁止:删签名、改签名、改语义;
- 破坏性变更只在 major 版本发生,且必须提供迁移指南与至少一个大版本的弃用期(
@Deprecated+ 告警)。
因此 API 类型上不再有「实验性」标注:凡是出现在类型页里的签名,都是 3.x 内不会破坏的承诺面。少数语义上仍可能微调的成员会在其小节里单独指出。
历史沿革:1.x 的只增不破承诺已按约在 2.0 兑现终止(
NoteTypeDefinition的四个行为旗标方法删除、改为语义标记子接口);2.x 的承诺在 3.0 兑现终止——3.0(3D-native 收口)删除 2D 几何残留参数(旋转 pivot 模式枚举、setLaneZ/setFakeTrackZ、createFakeTrack的 z);对照均见下文迁移对照。3.0 起进入新的 3.x 只增不破线。
1.x → 2.0 迁移对照(内置类型真实现化)
变化:行为分类从「NoteTypeDefinition 的四个 default 布尔旗标」改为「四个语义标记子接口」。内置五类型不再是空壳:判定/渲染/解析与模组类型走同一条分派管道(isBuiltinContent 命名空间门摘除,判据改为 BuiltinContent 身份标记——宿主内部概念,模组无感)。
旗标 → 子接口映射:
| 1.x(已删除) | 2.0(改为实现) | 复用的宿主行为路径 |
|---|---|---|
isHold() { return true; } | implements HoldNoteType | 长按状态机:起点判定 + endTime 终点按住/未按住(含 grace)+ 三段式拉伸渲染 |
isBarrier() { return true; } | implements BarrierNoteType | 多轨道横条 + 多次击打摧毁(轮次去重) |
isExploit() { return true; } | implements ExploitNoteType | 判定后生成 Payload 自动轨道 |
isPayload() { return true; } | implements PayloadNoteType | 跟随 Exploit 自动判定(不参与按键匹配) |
| (全部默认 false) | (不实现任何标记) | Tap 语义 |
迁移前后对照:
// 1.x // 2.0
public final class GripNote public final class GripNote
implements NoteTypeDefinition { implements HoldNoteType {
@Override public boolean isHold() { → // 语义由标记声明,删掉旗标方法
return true; // 其余(deserialize/render/...)不变
} }
} // 上下等价;标记子接口仍是 NoteTypeDefinition- 迁移是机械替换:删掉旗标方法 override,把
implements NoteTypeDefinition换成对应标记子接口(可再叠加其它接口);编译器会逐个指出残留的@Override isHold()等错误。 - 只实现标记、不再有方法:本批四个子接口均为空标记(后续可加配置方法,只增不破)。
model.NoteData(宿主侧)与context.NoteView(模组侧只读视图)的isHold()/isBarrier()/isExploit()/isPayload()只读方法保留,语义不变——模组侧判断代码零改动。- 内置
seecmd:hold/barrier/exploit/payload即按此声明(normal无任何标记);解析期deserialize对内置类型返回null(实现决策:内置主体由 Gson 直构,无 runtimeData 需求)。 - manifest 键
Seecmd-Api-Version: 1.0需改为2.0(major 不匹配会拒绝加载);缺键的模组按当前版本处理,但只要引用了被删方法,就会在编译期暴露。
1.x → 2.0 迁移对照(内置模式真实现化)
变化:内置游戏模式(seecmd:normal/autoplay/disorder)从「注册表放无行为描述符 + 场景内部类换实例」的双轨结构,改为注册表即活实例——注册表持有的就是被宿主 activeMode 持有的运行对象,内置/模组模式走同一条模式回调管道(无序机制的引擎驱动、专用输入分支、终点/Barrier/Payload 判定全部归位到模式对象,宿主不再旁路直调)。同时删除幽灵扩展点 GameModeDefinition.overridesJudgment()。
模组侧影响:零迁移成本——本批破坏面只有一条全仓无调用的 @Deprecated 幽灵方法,行为分类与本接口的其余成员不变:
| 1.x(已删除) | 2.0 替代 | 说明 |
|---|---|---|
GameModeDefinition.overridesJudgment()(@Deprecated) | 无需替代 | 幽灵扩展点(G3):宿主从不读取返回值(注释自认"返回 true 不会被读取");判定级接管由 NoteTypeDefinition.overridesJudgment() + onJudgeTick(音符类型级)与 onHandleInput(模式级输入消费)承担。若旧代码 override 了它,删掉即可(编译器会报错定位) |
| (无对应旧面) | GameModeContext.services() + GameModeServices(spi 包,加性) | 宿主注入的模式服务标记接口,仅供宿主内置模式注入使用,模组也可读;返回 null 表示未注入。模组通常无需消费 |
宿主侧语义锚点(模组可观测的等价性):
- 注册表
gameModes().get(seecmd:autoplay)取出的对象与宿主激活模式为同一实例(运行时绑定,e2e 有身份断言)。 - 内置模式的无序机制(谱面
disorderMarkers驱动,与所选模式正交)在模组模式激活期间同样工作——引擎驱动作为常驻模式挂点分发,与 1.x 行为逐毫秒一致。 mapLane/mirrorsPlayfield/onHandleInput消费次序不变:先当前激活模式,后无序机制窗口,最后常规输入。
2.x → 3.0 迁移对照(三期 3D-native 收口:删除 2D 几何残留参数)
变化:三期把渲染管线统一到 3D-native 后,2D 时代的几何残留参数已无执行体,随 3.0 一次性删除(不保留兼容别名)。动机:画家算法 z 层级(P-3 DN3-1)与"旋转圆心模式枚举"(P-3 DN3-3)都已从渲染器删除,接口若继续保留则只剩"传了也不生效"的假参数,会误导模组作者。
| 2.x(已删除/改签名) | 3.0 替代 | 说明 |
|---|---|---|
GameStateAccess.setLaneRotation(int, double, int pivot) | setLaneRotation(int, double) | 圆心固定为世界坐标的判定线中点(真轨本地 (0,0,0) = 世界原点);旧 TRACK_CENTER 等价形态改用 EntityPose.rotateZ(deg, pivotX, pivotY) 显式给圆心 |
GameStateAccess.LANE_ROTATE_PIVOT_JUDGE_LINE / LANE_ROTATE_PIVOT_TRACK_CENTER | 无(常量删除) | 圆心不再是"模式枚举" |
ExtraContentApi.setLaneRotation(int, float, String pivot) | setLaneRotation(int, float) | 同上;弧度单位不变 |
ExtraContentApi.setLaneZ(int, int) | 无(删除) | 画家算法 z 层级已随 P-3 整体移除;前后关系改由真 3D 深度(lane_pose.z / EntityPose.translate3D)表达 |
ExtraContentApi.setFakeTrackZ(int, int) | 无(删除) | 同上 |
ExtraContentApi.createFakeTrack(int id, int originLane, float alpha, int z) | createFakeTrack(int id, int originLane, float alpha) | 同上(z 形参删除) |
manifest Seecmd-Api-Version: 2.0 | 3.0 | major 不匹配会被拒绝加载;缺键按当前版本处理,但引用被删方法会在编译期暴露 |
GameStateAccess.setLaneOffset(int, double, double) | setLaneOffset(int, double, double, double) | 扩展:第三参为深度 z(px,+z 向屏幕内);旧调用补 0 像素输出不变 |
ExtraContentApi.setLaneOffset(int, float, float) | setLaneOffset(int, float, float, float) | 同上 |
ExtraContentApi.setNoteTransform(long, float, float, float rotationRad, float scale) | setNoteTransform(long, float dx, float dy, float dz, float pitchDeg, float yawDeg, float rollDeg, float scale) | 扩展为 3D 位姿:rollDeg = Math.toDegrees(rotationRad),其余补 0;三轴皆 0 且 dz=0 时逐字节等价 |
| (无) | GameStateAccess.project / unproject / cameraPose | 3.0 新增能力:世界↔屏幕换算与位姿读回;不暴露视图/投影矩阵(MatrixStack 无裸矩阵乘法原语,且宿主约定不应外泄) |
仓内迁移样本:mods-aimtrack 的 3 处 setLaneRotation(..., LANE_ROTATE_PIVOT_JUDGE_LINE) 改为两参调用、2 处 setLaneOffset(x, y) 补 0;官方示例模组 的 setLaneOffset 补 0、setNoteTransform 按 rollDeg = toDegrees(NOTE_ROT) 改写。以上均为签名适配,行为零变化。
不变:getLaneRotation / laneRotationPivot / trackAreaGeometry / setLaneScale / setLaneSkew / setLaneArc / setTrackOffset / EntityPose / MatrixStack 的签名与语义均不变(laneRotationPivot 的坐标口径自 2026-10-17 起为世界坐标:含单轨 lane_pose 偏移、不含 setTrackOffset;真轨本地 (0,0,0) = 世界原点)。setLaneOffset 与 setNoteTransform 属于扩展(新增 z / 三轴旋转),旧调用补 0 即可。
API 版本协商(manifest 键 Seecmd-Api-Version)
模组在 jar 的 manifest 里声明自己期望的 API 主版本:
| 声明情况 | 宿主行为 |
|---|---|
| 键缺失 | 按宿主当前版本处理(老 jar 不因缺键被一刀切) |
| major 不同 | 拒绝加载,错误文案给出「此模组要求 API X,宿主支持 Y」 |
| minor 更高 | 告警放行(宿主保证 minor 向后兼容) |
| patch 不同 | 不参与判定 |
校验发生在创建模组类加载器之前,因此不会出现"加载到一半才炸"的情况。校验逻辑与版本真源见 ModApiVersion。
类加载隔离
每个模组 jar 由宿主的独立类加载器加载,规则是:
java.*/javax.*/jdk.*/sun.*/com.sun.*/com.seecmd.mod.api.*/com.google.gson.*交给父加载器;- 其余类优先在模组自己的 jar 内查找(因此模组可以用
com.seecmd.mod.<自己的 id>这样的包名); - 未命中且属于
com.seecmd.*的类(即宿主/内核实现)→ 抛「内核访问被拒绝」。
含义:模组看不到也拿不到内核实现类,只能通过本文的 API 交互。若你确实需要宿主做某件事,正确的做法是走能力接口提出需求,而不是尝试反射内核。
常见报错与排查
| 现象 | 原因 | 处理 |
|---|---|---|
模组列表里状态是 LOAD_FAILED,原因写「此模组要求 API X,宿主支持 Y」 | manifest 的 Seecmd-Api-Version 与宿主 major 不匹配 | 改用与宿主主版本一致的 API jar 重新编译;或改对 API_VERSION / manifest 字段 |
LOAD_FAILED,原因含「引用了内核内部实现」 | 模组代码 import 了 com.seecmd.* 下的宿主/内核类 | 只依赖 com.seecmd.mod.api.*;需要宿主做某件事时走能力接口 |
抛出 RegistryFrozenException | 注册写在了 onInit / onPostInit / 渲染回调等非注册窗口里 | 把全部 ctx.registries().xxx().register(...) 移到 onPreInit |
IllegalArgumentException:非法命名空间 / 非法路径 | ResourceLocation 只接受小写 [a-z0-9_.-](命名空间)与 [a-z0-9/._-](路径) | 全部改小写;命名空间约定 = 模组 id |
IllegalArgumentException:该 location 已注册 | 同一 ResourceLocation 注册了两遍 | 注册前用 Registry.contains(location) 自检;不同内容用不同 path |
加载图片/文本时抛 ResourceNotFoundException | 资源没放在 src/main/resources/assets/<模组id>/...,或路径写错 | 1.0 没有裸路径回退,路径必须以 <模组id>: 开头并对应 assets/<命名空间>/<路径>;也可用 ResourceManager#list 列出 jar 内实际条目 |
编译报错「找不到符号 com.google.gson.JsonObject」 | 只引入了 mod-api jar,没引入 gson | 编译期把 gson-2.10.1.jar(或相近版本)加进 classpath;模板 build.bat 会自动找 |
ctx.language().get(key) 在 onInit 里读不到自己的译文 | 宿主在本模组 onInit 返回之后才扫描 jar 内的语言文件 | 放到 onPostInit(或更晚)再读;也可改用 ctx.language().add(locale, entries) 主动注册 |
| 改了模组却"没生效" | 注册在启动期一次性完成,启用/停用模组需要重启 | 重启游戏;模组列表里的开关只在下次启动生效 |
| 事件监听器抛异常后"什么都没发生" | 总线对每个监听器独立 try/catch(Throwable),异常被隔离并记日志 | 到游戏日志里搜该模组的 logger 名;异常不会影响其它模组,也不会中断游戏 |
| 想知道某个模组为什么没加载 | — | 经 ModManagerAccess#loadError(id) 取失败原因(含条目失败与 jar 级失败) |
与 0.7.x 旧 API 的差异
0.7.x 的模组在 1.0 宿主下不会被兼容加载。逐项对照表、迁移顺序与常见报错见单独的迁移指南:
1.0 迁移指南(源码仓库内文档)—— 旧 → 新 逐项对照与迁移步骤
类型速览
| 类型名 | 一句话作用 | 可用阶段(onPreInit / onInit+ / 运行期) |
|---|---|---|
Registry<T> | 冻结式注册表接口:注册窗口写入、冻结后只读 | onPreInit 写入;运行期只读 |
RegistryKey<T> | 注册内容的唯一身份(资源标识 + 值类型) | onPreInit 构造;运行期可读 |
ResourceLocation | namespace:path 形式的资源标识,替代裸字符串 id | onPreInit;运行期(通用标识) |
RegistryFrozenException | 注册表已冻结仍尝试写入时抛出 | 运行期(含热启用模组的 onPreInit) |
ModRegistries | 全部注册表的统一入口 | onPreInit |
NoteTypeDefinition | 音符类型定义:解析 + 行为 + 渲染三件套 | 运行期(谱面解析、每帧判定与渲染) |
NoteData | api 侧运行时音符基类,与内核模型解耦 | 运行期(解析期写入、运行时读取) |
NoteParseContext | 谱面解析上下文(轨道数 / BPM / 缩放) | 解析期(deserialize / onCreateRuntime) |
NoteRenderContext | 音符渲染上下文(位置、时间、轨道、皮肤图) | 运行期(每帧 render) |
JudgeInput | 音符到达判定线时的判定输入 | 运行期(判定时) |
JudgeInputState | 每帧判定回调的输入快照(按下/按住/松开) | 运行期(每帧判定) |
JudgeSink | 判定结果收集器 | 运行期(判定时) |
JudgeContext | 判定上下文:只读游玩状态与本类型待判定音符 | 运行期(每帧判定) |
JudgedNoteRenderContext | 判定文字渲染上下文 | 运行期(判定特效层) |
GameModeDefinition | 游戏模式定义(进入/更新/输入/轨道映射/覆盖渲染) | 运行期 |
GameModeContext | 游戏模式回调上下文(最小只读状态) | 运行期 |
JudgmentKindDefinition | 判定等级定义(分数权重、准度权重、combo 行为、特效) | 运行期 |
ComboBehavior | 判定等级对连击的影响行为枚举 | 运行期 |
ChartExtensionParser | 谱面 extensions 段解析器 | 运行期(谱面加载) |
ChartParseContext | 扩展段解析上下文(谱面 JSON、轨道数) | 运行期(谱面加载) |
ChartRuntimeContext | 谱面运行时上下文读写 | 运行期(谱面加载) |
CollectibleDefinition | 模组收集品定义(不可变值对象) | onPreInit 构造并注册 |
JudgmentPolicy | 判定窗口策略(毫秒) | onPreInit 注册;运行期由宿主读取 |
JudgmentAdjuster | 判定结果改写钩子(链式) | onPreInit 注册;运行期判定时调用 |
JudgeResult | 判定结果只读视图 | 运行期 |
JudgeResults | 判定结果改写工具类 | 运行期(判定改写时) |