更新日志
更新日志
模组系统的版本变更记录:新增 API、行为变化、THS 语言更新与问题修复(新版本在前)
本页记录 Theseus 模组系统的版本变更,重点面向模组开发者:新增了哪些 API、哪些行为发生了变化、修复了哪些问题。升级游戏版本后,建议通读对应条目的「行为变化」一节,确认既有模组是否需要调整。
4.0.0
真轨坐标约定变更(破坏性)。真轨本地 (0,0,0) 现在就是世界原点,所有轨道不再按车道序号自动向右排开;位置一律由谱面的 lane_pose / track_pose(或模组侧 setLaneOffset / setTrackOffset)提供。宿主版本 3.0.0 → 4.0.0(com.seecmd:seecmd-core 同版本发布);模组 API 版本保持 3.0 不变(Seecmd-Api-Version: 3.0 无需改动)。
- 发布日期:2026-10-17
- 对比基线:宿主 3.0.0
- 接口状态:宿主 / 编辑器依赖面破坏性变更,按
seecmd-core版本纪律 major + 1
破坏性影响
- 旧谱面必须迁移:既有谱面(均未写
lane_pose)的全部轨道会堆叠在世界原点(判定线在世界 y = 0、轨道体向上出画)。制谱者须给每轨写lane_pose(如x = 60 + 100 * i、y = 判定线 y)或使用迁移脚本;整区偏移用track_pose。 - 读面语义变更(编辑器依赖面):
TrackRenderer.getLaneX/getEffectiveJudgeLineY/getRotationPivot的返回值由"本地/布局偏移"改为世界坐标(默认未写lane_pose时均为 0)。 LaneView.height语义变更(编辑器依赖面):真轨height改为实际轨道体高度(judgeLineY + heightExtension,底边即判定线),不再表示轨道区域高;x()/y()同步为世界坐标口径。- 假轨
lane:N基准变更:FakeTrackRenderer的mount="lane:N"不再叠加原按序号槽位的O_i,只继承该轨lane_pose的平移(getLaneX/getEffectiveJudgeLineY)。 - 编辑器依赖面:
SceneRenderer/TrackRenderer/ExtraContentRenderer/GameplayLayout/CameraState/JsonUtil/Translator/ModManager/SkinManager/SurfaceImage/NoteSkin/ExtraContentData中,TrackRenderer三个查询方法、LaneViewGeometry与FakeTrackRenderer的本批变更必须在编辑器侧按世界坐标口径重新适配。
不变的约定
- 模组 API 仍为 3.0,公共签名无增删;
lane_pose.x/y本就是世界坐标语义,只是默认基准从"槽位中心"改为"世界原点"。 - 判定与输入路径零消费轨道几何;渲染数学除基准平移外未改,14 个 gameplay 黄金帧
diffPixels=0。主菜单两帧仅因侧栏版本号文本.0.0 → .0.0产生差异(按发布流程需重录基线)。
3.0
三期 3D-native 统一管线的收口版本。宿主渲染管线早已收敛到真 3D(画家算法 z 层级与"旋转圆心模式枚举"在 3.0 之前的开发批次中已从渲染器删除),本版把接口上只剩"传了也不生效"的 2D 残留参数一次性删掉,避免误导模组作者。宿主版本同步升到 3.0.0(2026-10-17 编辑器适配批:嵌入面公共签名再次破坏性变更,按语义化纪律 major + 1)。
- 发布日期:2026-10-17
- 对比基线:2.0 / 宿主 2.0.0
- 接口状态:破坏性变更随 3.0 发生(迁移对照见 API 参考 · 2.x → 3.0 迁移对照);自 3.0 起公共签名再次冻结(3.x 内只增不破)
破坏性变更(随 3.0)
| 旧(2.x) | 新(3.0) |
|---|---|
GameStateAccess.setLaneRotation(int, double, int pivot) | setLaneRotation(int, double rotationRad)——圆心固定判定线中点 |
GameStateAccess.LANE_ROTATE_PIVOT_JUDGE_LINE / LANE_ROTATE_PIVOT_TRACK_CENTER | 删除(圆心不再是模式枚举);需要自定义圆心用 EntityPose.rotateZ(deg, pivotX, pivotY) |
ExtraContentApi.setLaneRotation(int, float, String pivot) | setLaneRotation(int, float rotationRad) |
ExtraContentApi.setLaneZ(int, int) / setFakeTrackZ(int, int) | 删除(画家算法 z 层级已整体移除;前后关系由真 3D 深度表达) |
ExtraContentApi.createFakeTrack(int id, int originLane, float alpha, int z) | createFakeTrack(int id, int originLane, float alpha) |
manifest Seecmd-Api-Version: 2.0 | 需改为 3.0(major 不匹配会拒绝加载) |
GameStateAccess.setLaneOffset(int, double, double) | setLaneOffset(int, double, double, double)——新增深度 z(px);旧调用补 0 像素不变 |
ExtraContentApi.setLaneOffset(int, float, float) | setLaneOffset(int, float, float, float) |
ExtraContentApi.setNoteTransform(long, dx, dy, rotationRad, scale) | setNoteTransform(long, dx, dy, dz, pitchDeg, yawDeg, rollDeg, scale)——扩展为 3D 位姿(rollDeg = toDegrees(rotationRad));其余补 0 逐字节等价 |
新增能力(3.0,加性)
| 新 API | 说明 |
|---|---|
GameStateAccess.project(x, y, z) | 内容坐标 → 屏幕坐标 {screenX, screenY, ndcDepth}(相机后方返回 null) |
GameStateAccess.unproject(screenX, screenY, z) | 屏幕坐标 → 指定深度平面上的内容坐标 {x, y, z}(3D 拾取用) |
GameStateAccess.cameraPose() | 当前生效相机位姿 {x, y, z, yawDeg, pitchDeg, fovDeg}(fovDeg = NaN 表示 AUTO) |
宿主 / seecmd-core 3.0.0(嵌入宿主,2026-10-17)
宿主版本 2.0.0 → 3.0.0;com.seecmd:seecmd-core(宿主瘦包)同版本发布,供独立谱面编辑器直接复用渲染内核。
SceneRenderer门面扩展:Frame新增可选camera(CameraState)与extraRenderer(ExtraContentRenderer)。嵌入方传非 null 即可一次renderWorldContent复现"相机矩阵 + 世界内容层",无需照抄游戏侧装配顺序;两者传null时行为与旧版逐字节一致。- 图片皮肤注入点:
SurfaceImage.textureId()公开;GlSurfaceImageFactory公开;SkinManager.loadImageSkin(File, String)公开。嵌入宿主可用自己的 GL 纹理构造NoteSkin,不必依赖宿主的图片加载路径。 - 死字段清除:
EntityDeformConfig.easing删除(形变动画只认animation.move.easing,与其余 Config 一致)。 - 谱面 content 侧:实体真 3D(
z/pitchDeg/yawDeg/rollDeg)、entity_destroy、mount="entity:ID"父子挂载与级联销毁、lane_alpha/ui_alpha/camera_pose的null = 不改变语义修正——详见下节。 - 编辑器依赖面固定清单(这些类的公共签名变更必须在本日志标注):
SceneRenderer(含Frame/Host)、TrackRenderer、ExtraContentRenderer、GameplayLayout、CameraState、JsonUtil、Translator、ModManager、SkinManager、SurfaceImage、NoteSkin、ExtraContentData。
谱面 content 类型(3.0 收口后 14 种)
- 删除
textbox:名称与实际能力不符(textboxStyle的 alignment / borderColor / borderWidth / fadeOutPrevious 四个字段零消费者,渲染只画两行字),且全库谱面零使用。其"上一行上移淡出"机制(previousText*+ 倒带闭式推导)一并删除,只保留text。 text新增 3D 投影锚点:position.anchor可取"lane"/"entity",配attachId与可选attachDepth;文本仍为正交屏幕字体,位置由当前世界相机投影得到(与GameStateAccess.project同源)。缺省锚点与旧谱面逐字节一致。- 实体真 3D + 自定义父子(2026-10-17):
entity_create新增z/pitchDeg/yawDeg/rollDeg;entity_pose新增z/pitchDeg/yawDeg与start*(rotationDeg语义为 roll);track 的mount支持"entity:ID"(父先建、禁环、深度 ≤16,继承父链变换与 alpha 相乘)。 - 新增
entity_destroy(第 14 种):kind="track"级联销毁子孙与挂载音符,kind="note"只删音符。 - 语义修正:
lane_alpha/ui_alpha的alpha可为 null(= 不改变);camera_pose的 x/y/z/yaw/pitch 可为 null(= 不改变,只改 yaw 不再重置位置);5 个 Config 上的死字段easing已删除(只认animation.move.easing;EntityDeformConfig的残留字段已于 2026-10-17 清除);seq不是字段(同刻按 JSON 数组顺序)。 - 其余类型见 制谱器开发指南 · content;机器可读字段表见仓内
content-schema.json(由verify/ContentSchemaExport.java生成、verify.batStep 15 对账)。
为什么不是直接给投影矩阵?模组绘制走
RenderCanvas/MatrixStack(顶点由宿主变换),MatrixStack没有"乘裸矩阵"的原语;裸矩阵既无法直接使用,又会把宿主的坐标/投影约定固化成模组契约。project/unproject与渲染同源,宿主换投影实现时零破坏。
行为变化
- 旋转圆心一律为判定线中点(本地原点派生)。旧
pivot = TRACK_CENTER的等价形态(绕轨道几何中心转)请改用EntityPose.rotateZ(deg, pivotX, pivotY)显式给圆心。 laneRotationPivot(int)的坐标口径不变(轨道区绝对坐标:含单轨偏移、不含setTrackOffset),但不再有"最后设置的模式"语义。- 渲染像素无变化:本版只删签名 / 删无效参数 / 新增能力,不触碰绘制数学(
menu-main/menu-child仅因宿主版本号文本由1.0.0变为2.0.0而重录基线;P-4b 的 z / 三轴扩展在全 0 时逐字节等价,13 帧黄金帧diffPixels=0)。
2.0
模组自绘进入 3D——MatrixStack 新增 z 通道原语(translate3D / scale3D / rotateX / rotateY,与 2D 同栈可交错),并把轨道 / 相机的位姿调整统一为可链式叠加的 adjuster 注册表(trackPoseAdjusters() / cameraPoseAdjusters(),宿主默认位姿沿链可变传阅、原地修改),配套相机位姿通道(GameStateAccess.setCameraPose / setCameraPoseAuto + render.CameraPose)。同时:模组设置项获得自动独立标签页(有页面设置项的模组在设置页各占一页,锚点定位废弃)、打击特效样式改为多提供者可组合(全部非 null 提供者同时生效)、ModRegistries 补 layerOverrides() / customRenderLayers() / judgmentWindowSynthesizers()、UiCustomization 增设光标接管。2.0 也是 mod-api 的首个破坏性变更点:NoteTypeDefinition 的四个行为旗标方法(isHold / isBarrier / isExploit / isPayload)删除、改为语义标记子接口,旧模组按「1.x → 2.0 迁移对照」机械替换即可。
- 发布日期:2026-10-12(3D 原语 / 相机位姿 / 位姿链 2026-10-11,设置页独立标签页 2026-10-12)
- 对比基线:1.0.0
- 接口状态:破坏性变更随 2.0 发生(1.x 的只增不破承诺已按约终止,迁移指南见 API 参考 · 1.x → 2.0 迁移对照);自 2.0 起公共签名再次冻结(2.x 内只增不破)
破坏性变更(随 2.0)
| 旧(1.x) | 新(2.0) |
|---|---|
NoteTypeDefinition.isHold() / isBarrier() / isExploit() / isPayload()(default 布尔旗标) | 改为 implements HoldNoteType / BarrierNoteType / ExploitNoteType / PayloadNoteType 语义标记子接口,宿主行为路径不变 |
manifest 键 Seecmd-Api-Version: 1.0 | 需改为 2.0(major 不匹配会拒绝加载);NoteView / NoteData 的 isHold() 等只读方法保留、模组侧判断代码零改动 |
新增 Mod API
- 模组自绘 3D 原语(
MatrixStackz 通道,2026-10-11):translate3D(dx, dy, dz)/scale3D(sx, sy, sz)/rotateX(angle, pivotY, pivotZ)/rotateY(angle, pivotX, pivotZ),与既有 2D 原语同栈、可交错书写。口径:角度弧度、z 像素且 +z 向屏幕内、硬边界 |z| ≤ 2000(推荐 ≤ 500);仅世界层(BACKGROUND/WORLD/NOTES/JUDGE_FX及 index < 4 的自定义层)进入深度场,层外安全退化;dz==0/sz==1/ 0° 走逐位相同的 2D 快路径。自由虚拟相机可由原语组合(rotateX(-pitch,0,0)→rotateY(-yaw,0,0)→translate3D(-ex,-ey,-ez)恰构成逆 view 矩阵)。 - 相机位姿通道(2026-10-11):
GameStateAccess.setCameraPose(x, y, z, yawDeg, pitchDeg, fovDeg)+setCameraPoseAuto(),配合render.CameraPose(FOV_AUTO哨兵)。角度单位度、相机默认位于内容平面正前方(z 为负)、每帧可写后写生效、fovDeg传CameraPose.FOV_AUTO用宿主现行推导(d = 内容高 × 0.5)、近裁剪面 = d/3。绝对写后相机接管直至倒带 / 新谱面复位,setCameraPoseAuto()是唯一干净的还原入口。 - 位姿调整器链(2026-10-11,统一世界模型):
ModRegistries.trackPoseAdjusters()/cameraPoseAdjusters()两条注册表——宿主每帧构造默认位姿(谱面动画 + 既有 setter 通道合成值;相机默认 = 直视轨道推导),沿链可变传阅、原地修改(前一个输出即后一个输入,大幅移动与小颤抖天然共存),链尾交给渲染;TrackPose/CameraPose为可变传阅对象、禁跨帧持有引用。确定性契约(纯函数、禁随机 / 墙钟 / 网络)与异常隔离(单个 adjuster 抛异常被跳过、链继续)同既有约定;无注册者时快速路径直通、渲染逐位不变。 - 模组设置项自动独立标签页(2026-10-12):有页面设置项(
SettingsExtension.addItem)的模组各获得一个自动标签页(标题 = 模组显示名),其全部页面项归位到该页;自动页排在宿主页之后、显式自创页(addTab)之前;无页面项的模组不产生页,集合成员仍由JUMP项钻取抵达。持久化键modId.itemId与页归属无关,旧settings.json零迁移。 ModRegistries新增注册表:layerOverrides()(渲染层默认绘制替换声明)、customRenderLayers()(自定义渲染层)、judgmentWindowSynthesizers()(判定窗口合成器,在「逐字段取最大」合并之后链式应用,可收紧 / 非对称化窗口;无注册项时行为零变化)。UiCustomization光标接管:addCursorRenderer(String id, CursorRenderer renderer)+ 嵌套CursorRenderer/CursorState——按注册顺序取首个整体替代引擎默认光标绘制,回调每渲染帧至多一次。SettingsItem暂存(draft)协作:新增staged()/isDirty()/onCommit()/onRevert()(加性default;默认staged()=false= 保持既有「即时生效、取消不回滚」语义),供设置页底栏「确定 / 取消 / 应用」纳入模组项。LaneView新增 11 个只读几何量(2026-10-15,统一世界模型二期 / ):LaneView新增heightExtension()(高度延伸量)、skew()/skewAnchorY()(歪斜量 / 歪斜锚点 Y)、arcBend()/arcCenterY()(弧形弯曲量 / 弧心 Y)、alpha()(背景透明度)、keyIndicatorAlpha()(键框透明度,真轨与假轨均已独立)、scaleX()/scaleY()(缩放)、rotationPivotX()/rotationPivotY()(旋转圆心的画布坐标,计入trackOffset)。真轨与假轨同源派生(同一几何实现),已安装形变但未设置时取各自默认值。全部为加性新增(2.x 内只增不破)。- 假轨道几何原语按
id寻址(2026-10-15,统一世界模型二期 / ):ExtraContentApi新增setFakeTrackSkew(id, skew, anchorY)/setFakeTrackArc(id, bendDir, bendAmp, arcCenterY)/setFakeTrackHeight(id, height)/setFakeTrackKeyIndicatorAlpha(id, alpha)与setFakeTrackRotation(id, rotationRad, pivot)重载,把假轨道的形变 / 高度延伸 / 键框透明度 / 旋转圆心补齐到与真轨同口径(此前假轨只能改 offset / alpha / scale / rotation)。钳制语义与真轨对应原语逐项一致、共用同一实现;id不存在时限频告警并忽略;纯视觉、零判定副作用。 - 模组可见实体层级 API(2026-10-16,统一世界模型二期,查询面):
GameStateAccess新增 11 个default方法(createEntity/mountToTrackArea/mountToTrack/mountToEntity/entityPose/entityWorldMatrix/entityVisible/setEntityVisible/entityAlpha/setEntityAlpha/destroyEntity)与两个类型——render.SceneEntity(不透明句柄,无方法)与render.EntityPose(每 tick 本地位姿写入面)。模组可创建自有实体并挂进宿主世界层级(轨道区 / 轨道 / 自有实体之下),每帧写入本地位姿后查询世界矩阵自行变换自绘内容(查询面:实体 = 纯变换节点,宿主不按树渲染模组回调)。装配类操作(mountTo*/destroyEntity)仅在非 tick 上下文合法(如GameStartEvent回调);entityPose仅在GameplayTickEvent分发期间合法且每 tick 清空(未写 = 恒等、帧间不累积, 确定性);实体归创建方模组独占(改他人实体被拒绝),随游玩场景存续、游玩结束宿主级联销毁。全部为加性新增(2.x 内只增不破)。
行为变化
- 打击特效样式改为多提供者可组合(阶段 2,兑现「禁止首个非 null 生效」):
hitEffectStyles()下全部返回非 null 样式的 provider 按注册顺序同时生效、依次绘制(各自以自身durationMs独立消解强度);旧「第一个非 null 整体生效、其余被遮蔽」的语义已移除,多模组叠加特效不再互相顶掉。 - 模组设置项定位:原「插入到指定原生设置项前 / 后」(
anchorBefore/anchorAfter锚点)不再被设置场景消费(签名保留、字段被忽略),改为一律归入模组自动独立页。 - 世界层外
rotateX/rotateY的语义由「完全无效果」修正为「安全退化为 2D 压扁」(不报错、不告警)。 - 宿主侧统一世界模型:
setTrackTilt3D归一为相机位姿 pitch 分量的语法糖(tilt 与相机位姿正交组合,画面效果数学恒等)。 LaneView.x()/y()口径修正为画布坐标(2026-10-15,统一世界模型二期 / §10 G4):x()/y()一律计入trackOffset(track_move/setTrackOffset)——此前真轨y()漏trackOffsetY、假轨x()/y()均漏。矩形本身仍为未旋转布局矩形(lane 偏移与 skew/arc/height 形变不烘焙进矩形,作为独立只读量暴露);rotationPivotX()/rotationPivotY()同为画布坐标。以「轨道中心 =x() + width() / 2」定位的模组(仓库内 hint HUD / aimtrack)结果不变。LaneView.width()修正为每轨宽(2026-10-15,统一世界模型二期 / ):真轨width()此前误取trackWidth / laneCount(偏窄laneCount倍,x()随之右偏);修正后真轨与假轨同为TrackRenderer.getTrackWidth()(相邻轨中心间距)。height()含义不变(真轨 = 轨道区域高、假轨 = 背景高)。直接按轨宽对齐的模组会复位到正确值。- 假轨形变谱面原语(2026-10-15,统一世界模型二期 / ):谱面额外内容新增
fake_track_skew/fake_track_arc/fake_track_height,与真轨lane_skew/lane_arc/lane_height同口径(此前 装的假轨形变能力只有模组能用,谱面侧无入口)。默认参数零变化:不写这三个原语的谱面逐像素不变。 LANE_ROTATE_PIVOT_TRACK_CENTER标记@Deprecated(2026-10-15,统一世界模型二期 ):GameStateAccess.LANE_ROTATE_PIVOT_TRACK_CENTER降格为导入谱面的兼容映射,标@Deprecated——仅符号标记、行为不变(依裁定 不真删,真删须 3.0)。它等价于以判定线圆心旋转再叠加一个平移:rotate(θ, 判定线圆心) + translate(δ),δ = (I − R)·(c − p)(c= 轨道中心、p= 判定线圆心、R= 旋转矩阵、I= 单位矩阵);正典为LANE_ROTATE_PIVOT_JUDGE_LINE,新代码请统一使用。
修复
- 修复缝层
GlRenderSurface.multRight输出缓冲别名导致透视相机下rotateX/rotateY变换面与恒等 z=0 图元不光栅化的问题(输出改独立缓冲,与 2Drotate既有纪律同款;新增透视相机 + 90°/270° 真 z 位移面的自测断言)。 - 宿主渲染侧结案:
e2e快照整体色偏(源主为内置示例模组的全屏滤镜,非渲染缺陷)、满矩阵快照的多余暗化(滤镜模组全屏 overlay 的预期顶层混合)等。
1.0.0
模组 API 的一次性破坏性重制。API 抽为独立构件 com.seecmd:mod-api(可由 verify/ApiBoundaryCheck 强制「不依赖内核」),生命周期改为四阶段(onPreInit 注册窗口 → 冻结 → onInit → onPostInit),8 个独立注册表统一为 ctx.registries() + RegistryKey,游玩/全局两条事件总线合并为一条类型化事件总线,资源与语言改为 ResourceLocation 命名空间约定,渲染改为 RenderContext + 7 层 + 裸绘制原语。同时新增 API 版本协商(manifest 键 Seecmd-Api-Version)与模组类加载隔离。旧 0.7.x 模组不再兼容,逐项迁移见「迁移指南」。
- 发布日期:2026-09-20
- 对比基线:0.7.4
- 接口状态:模组 API 不再标注
@Experimental;com.seecmd.mod.api.*的公共签名自 1.0 起冻结(1.x 内只增不破,破坏性变更只在 2.0)
破坏性变更(全部为破坏性,无兼容层)
| 旧(0.7.x) | 新(1.0) |
|---|---|
Mod.onLoad(ModContext) | onPreInit(唯一注册窗口)/ onInit / onPostInit 三阶段 + onUnload |
GameplayMod.registerRenderFilters 等 5 个 per-模组注册回调 | 统一在 onPreInit 内经 ctx.registries() 注册 |
NoteTypeRegistry / GameModeRegistry / JudgmentKindRegistry / ChartExtensionRegistry(字符串 id) | Registry<T> + RegistryKey<T> + ResourceLocation |
ModEventBus / GlobalEventBus / ModEventBus.publish | 一条 EventBus + Event 层次 + EventPriority / EventResult,投递用 post |
RenderResources.loadImage(String) | ResourceManager.loadImage(ResourceLocation),路径 assets/<命名空间>/<路径>,无裸路径回退 |
NoteTypeDefinition.defaultImage(): BufferedImage | 返回 ImageResource/RenderImage 句柄 |
RenderCanvas.drawImage(RenderImage, …)(无子矩形 / 无矩阵栈) | RenderContext.canvas()/matrices(),drawImage 支持任意目标矩形 |
RenderLayer(2 槽) | 7 层 |
ModSceneAdapter.renderFx + JavaFX 画布 | 只留 renderSurface |
AWT Toolkit 剪贴板(api/text/TextField) | ClipboardAccess 能力 |
Mod.registerCollectibles(CollectibleRegistry) + CollectItem | onPreInit 内 ctx.registries().collectibles() + CollectibleDefinition |
com.seecmd:mod-api:0.7.4(瘦 jar) | com.seecmd:mod-api:1.0.0(独立模块)+ manifest 键 Seecmd-Api-Version |
新增
- API 版本协商:manifest 键
Seecmd-Api-Version;major 不等 → 拒绝加载并给出「此模组要求 X,宿主支持 Y」;minor 更高 → 告警放行;键缺失 → 按当前版本处理。 - 类加载隔离:每个模组 jar 由独立类加载器加载,只放行
com.seecmd.mod.api.*、JDK 与 gson;引用宿主/内核实现类会被拒绝并给出可读原因。 - 注册表冻结:注册窗口一次性;冻结后写入抛
RegistryFrozenException,重复注册同一ResourceLocation抛IllegalArgumentException(不再是静默忽略)。 - 资源与语言:
ResourceLocation命名空间约定 + 「磁盘覆盖目录 → jar 内」两级查找;语言文件assets/<模组id>/lang/<locale>.json由宿主自动扫描。 - 格式与外观:渲染滤镜 / 背景渲染器 / 打击特效样式统一注册入口;
RenderContext提供层、矩阵栈与帧信息。
行为变化
- 玩家在模组列表里启用/停用模组需要重启游戏才生效(注册在启动期一次完成)。
- 找不到资源时抛
ResourceNotFoundException,不再返回null。 - 不可取消事件上调用
setResult是无操作(纯通知事件不会被模组"吞掉");监听器抛异常由总线隔离,不影响其它模组。 - 加载失败原因可经
ModManagerAccess.getLoadError(id)读取,模组列表显示LOAD_FAILED。
物料
- 发布物:
mod-api-1.0.0.jar(编译期依赖)、mods-template.zip(含可直接编译运行的ExampleMod示例:注册自定义音符类型 + 订阅 2 个事件 + 加载贴图 + 自带语言文件)。 - 文档:
mod-api.md重写为 1.0 权威签名;mod-guide.md重写为 1.0 上手路径;新增mod-migration-1.0.md(0.7.x → 1.0 逐项对照)。
0.7.4
新增进程级只读 GlobalKeyEvent 全局按键事件,为模组「跨游玩持续监听按键」提供稳定入口;如实标注场景级 InputAccess 每次游玩重建一次的限制,指引开发者按场景选择正确的按键获取方式。同时为设置场景新增「设置集合」交互,并提供在原生设置场景内插入模组设置项的 Mod API(SettingsExtension / SettingsItem),由新模组 mods-trackbg 提供参考实现。另新增音符类型自主判定接管(overridesJudgment / onJudgeTick)、运行时轨道几何控制(旋转 / 偏移)与音符扩展数据透传,由新模组 aim_track 提供参考实现。
- 发布日期:2026-08-31
- 对比基线:0.7.3
- 接口状态:全部 Mod API 仍标注
@Experimental(实验性,可能调整)
新增 Mod API
| 能力 | 入口 | 说明 |
|---|---|---|
| 全局按键事件 | ctx.globalEventBus() 订阅 GlobalKeyEvent | 进程级只读键盘按键事件(按下/释放沿),一次订阅、跨游玩有效,不受游玩场景重建影响。解决场景级 InputAccess 只能监听到当前这一次游玩的问题。见下方「全局按键事件」小节 |
| 真轨流速调节() | GameStateAccess.setLaneSpeedFactor(lane, factor) | 设置指定真轨道的流速倍率(乘在全局 scrollSpeed 上),任意值,可为负(反向下落),瞬时生效;纯视觉不影响判定。配套 getLaneSpeedFactor(lane) 读取。谱面原生调节见额外内容特效 lane_scroll,两者写同一份每轨倍率 |
| 轨道样式覆盖() | ctx.uiCustomization().addTrackStyleProvider(id, provider) | 游玩场景每帧、每条轨道(真/假)向模组查询样式覆盖:快捷键框(文本/颜色/可见性)、分隔线(颜色/可见性)、背景(颜色/可见性)可单独自定义。多模组按注册顺序首个返回非 null 的整体生效;提供器必须返回缓存样式对象(每帧每轨调用)。真轨 trackId 为 lane_0..,假轨为 fake_{id};画布原点假轨(originLane=-1)同样可覆盖。见 mod-api.md §4.8「轨道样式覆盖」 |
| 设置场景扩展() | ctx.uiCustomization().settings() | 将模组设置项直接嵌入原生设置场景(页面列表 / 集合面板),支持开关 / 数值 / 输入框 / 跳转集合 / 自绘制等类型。当时可在指定原生设置项前/后插入(锚点定位),后续版本改为模组设置项自动独立页、锚点不再被消费(见 2.0 条目)。见 mod-api.md §4.8「设置场景扩展」 |
| 一键多轨映射() | LaneInputMapper.mapKeyToLanes(keyCode) | 新增 default 方法:物理键 → 逻辑轨道集合,一次按键触发所在集合全部轨道(press 判定 / HOLD 保持 / 轨道高亮一致)。默认实现回退到既有 mapKeyToLane 单轨语义,旧实现无需修改。配合 系统音量键可实现旋钮分区玩法,参考模组 volume_sides(音量+触发左半边、音量-触发右半边,工程见仓库 mods-volumesides/)。见 mod-api.md §4.10 |
| 音符类型自主判定接管() | NoteTypeDefinition.overridesJudgment() + onJudgeTick(...) | 声明 overridesJudgment()=true 后,宿主对该类型音符不再做按键时间匹配,改为每帧调用 onJudgeTick(timeMs, state, ctx, sink) 由定义对象自主判定(连按、按序、角度匹配等任意规则),经 sink.accept(kind, time, note) 产出判定后走统一计分 / 连击 / 特效路径;超时兜底 MISS 仍由宿主负责。配套 JudgeInputState(本帧输入快照)与 JudgeContext(本类型待判定音符视图)。见 mod-api.md §3.2「自主判定接管」 |
| 运行时轨道几何控制() | GameStateAccess.setLaneRotation(lane, rad, pivot) / setLaneOffset(lane, dx, dy) | 游玩中实时设置真轨的旋转角度(弧度,可选判定线中点 / 轨道中心两种圆心)与平移偏移(canvas px),纯视觉不影响判定;轨道背景 / 分隔线 / 音符(含自定义类型)与顶端剔除窗口自动跟随变换。与谱面特效 lane_rotate / lane_move 写同一份每轨状态。配套 getLaneRotation(lane) 读取。参考模组 aim_track(音量旋钮控制轨道旋转瞄准音符) |
| 音符扩展数据透传() | NoteView.extensionData() / NoteRenderContext.extensionData() | 自定义音符类型在 deserialize / onCreateRuntime 经 NoteData.setExtensionData(data) 挂载的运行时数据,判定回调(onJudgeTick 的 JudgeContext.notes())与渲染回调(render 的 ctx)均可只读取回,判定与渲染共用同一份数据。宿主内置类型返回 null。见 mod-api.md §3.2 |
| 自定义音符类型皮肤兜底() | NoteTypeDefinition.defaultImage() + NoteRenderContext.skinImage() | 模组可为自定义音符类型提供默认皮肤图:当前皮肤没有该类型专属槽位(图片皮肤目录内 <类型key>.png)时,宿主渲染回退链变为「玩家皮肤专属槽位 → 模组 defaultImage() → 行为分类槽位(普通→normal 等)」,模组不再被动显示成普通音符外观。render 回调内可经 ctx.skinImage() 取宿主解析好的图(玩家皮肤优先、模组默认兜底),实现「外观交给皮肤系统、位置/方向交给模组」的组合(如音符在模组自算的位置上以皮肤图绘制并旋转对齐运动方向)。参考模组 aim_track |
| 游玩逐帧事件() | GameplayMod.registerEvents 订阅 GameplayTickEvent | 宿主在未暂停的每个游玩帧(输入处理后、判定分发前)发布,musicTimeMs() 提供当前音乐时间。供模组做帧率无关的逐帧动画/插值(如轨道旋转平滑逼近目标角度);暂停与入场过渡期间不发布。见 mod-api.md §7.2 |
| 原生轨道闪光() | GameStateAccess.flashLane(lane, durationMs) | 触发指定真轨道的原生按压高亮:与玩家按键时轨道指示器/轨道背景点亮完全同源(同一份按下状态),持续 durationMs 毫秒后自动熄灭,期间真实按键状态照常叠加,重复调用刷新持续时间。模组判定命中时无需自绘样式即可复用原生视觉反馈。见 mod-api.md §4.2 |
| 强制 altHUD() | GameStateAccess.setAltHudForced(forced) | 由模组强制启用/恢复备用 HUD(altHUD),与玩家「界面不透明度」滑杆状态无关,每局开始后调用一次,局结束自动恢复滑杆语义。altHUD 本轮同步新增判定等级显示:COMBO 数字右侧竖排 3 个小矩形(当前判定在最上、历史向下),颜色与标准 HUD 连击显示一致(perfect 绿 / great 蓝 / miss 红 / 自定义判定取定义色)。见 mod-api.md §4.2 |
| extensionData 排查提醒 | mod-api.md §3.1 重要提示 | 自定义音符 NoteData 子类重写 setExtensionData/extensionData 并做类型过滤会导致新载体类型被静默丢弃成 null:判定完全不产出(连超时 MISS 都没有)、渲染全部取默认值,且宿主无任何异常/日志,无游戏源码时几乎无法排查。文档已补充正确做法(不重写或无条件透传 Object)与排查线索 |
| altHUD 判定等级显示 | 宿主渲染(AltHudRenderer.renderComboFx) | altHUD 下 COMBO 数字右侧竖排最多 3 个判定等级小矩形(当前判定在最上、历史向下),与标准 HUD 连击显示同数据源、同配色(perfect 绿 / great 蓝 / miss 红 / 自定义判定取定义色);无序进度展开时随文本淡出。配合 setAltHudForced 强制路径,标准 HUD 面板在模组强制 altHUD 时整体隐藏(原生滑杆语义不变) |
| 自动播放适配示例 | 参考模组 aim_track(无新 API) | 模组经 GameplayContext.currentModeId() 识别自动播放模式(seecmd:autoplay),在判定回调中把最临近待判定音符的角度设为轨道目标角(提前 600ms 发出请求、逐帧平滑到线前对准),期间忽略玩家旋钮输入。鼠标控制启用时分流为虚拟光标链路:自动播放把光标缓动滑向音符方向(~50ms 时间常数,像人手移动),轨道朝向光标即对准;另设「自动播放失误率」设置项(0-100%),按音符掷骰施加过冲/延迟/急缓三类观赏性扰动(上限经收敛,拉满也不会真失误)。示范「模组自驱动画 + 既有查询 API」的组合方式 |
| 光标与旋转圆心查询() | GameStateAccess.cursorPosition() / laneRotationPivot(lane) | cursorPosition() 返回光标在游玩画布上的逻辑坐标(与 NoteRenderContext 同一空间,最后已知位置);laneRotationPivot(lane) 返回指定真轨道当前旋转圆心的画布坐标(含 lane_move 偏移,圆心模式为该轨道最后设置值,轨道无效返回 null)。两者组合可实现「轨道朝向光标」类交互(参考模组 aim_track 的鼠标控制设置项)。见 mod-api.md §4.2 |
| 虚拟光标写入() | GameStateAccess.setCursorPosition(x, y) | 设置虚拟光标位置:设置后 cursorPosition()、场景光标轮询与游戏自绘光标显示全部跟随该位置(虚拟光标位于输入管理层 MouseInputManager,三层同源;系统真实光标不动,不做 OS 级 warp);玩家移动真实鼠标(事件坐标 / 抓取位移)时虚拟光标立即失效,场景切换清除。与 laneRotationPivot 组合可实现「自动播放时移动光标 → 轨道朝向光标」的全链路自动化(参考模组 aim_track:鼠标控制 + 自动播放同时启用时,自动播放把光标放到音符方向上,轨道朝向光标即对准音符)。见 mod-api.md §4.2 |
| 光标改为直接控制游戏内位置( 语义调整,签名不变) | GameStateAccess.setCursorPosition(x, y) | 取消「虚拟光标覆盖层」:写入的就是 cursorPosition() 返回的那一份游戏内位置(与真实相对位移增量同源),自绘光标与场景轮询随之自然跟随,不再有「真实鼠标一动即失效」这一行为;玩家移动真实鼠标后照常在其基础上累加位移(后发生者生效)。模组若需与玩家共存,须自行检测真实输入并让位——参考模组 aim_track 已按此调整:回读位置 ≠ 自身上次写入值即判定玩家接管,让位 600ms。接口签名与既有代码兼容。见 mod-api.md §4.2 |
全局按键事件(GlobalKeyEvent)
- 已知问题:场景级
InputAccess(4.5 节)是「游玩一次、重建一次」的能力——宿主每次进入游玩才注册新实例,其subscribeKeyHandler订阅只对当次游玩有效,退出 / 重试 / 换曲即失效。因此 跨游玩的持续按键监听不能用InputAccess(在加载阶段订阅尤其必然收不到按键),它只适合在游玩进行中临时读键 / 消费键。 - 解决入口:
GlobalKeyEvent是全局总线上的进程级只读键盘按键事件,经ctx.globalEventBus().subscribe(GlobalKeyEvent.class, ...)订阅,一次订阅、跨游玩有效,不受场景重建影响。 - 只发布按下 / 释放沿(
KEY_PRESSED/KEY_RELEASED),按住(KEY_HELD)高频事件不转发,避免全局总线刷屏;需要持续按键状态仍查场景级InputAccess.isKeyDown。 - 只读通知,无
consumeKey(消费按键是游玩内语义,仍走InputAccess);订阅自动归属本模组 id,模组停用时宿主统一清理。
设置场景扩展(SettingsExtension / SettingsItem)
- 设置场景新增「设置集合」交互:点击带
>>>的跳转项后主设置面板左移、标题栏失焦变灰,右侧出现同风格集合面板,ESC返回。原生开发者测试项(历史栈 / 帧率 / 鼠标 / 悬浮 / 压测)已移入「开发者测试」集合。 SettingsItem六种类型(SWITCH/NUMBER/INPUT/JUMP/JUMP_VALUE/CUSTOM),覆盖原生设置项全部组件外观;模组项互动(移动 / 切换 / 跳转)带与原生一致的音效。值 / 态由模组自行持久化,宿主负责渲染与交互分发。- 生命周期:模组停用 / 游戏退出时宿主自动清理全部注册项,模组无需(也不应)在
onUnload逐个 remove。 - 参考实现:
mods-trackbg通过本能力注册开关控制原版轨道背景着色(奇数轨黑 / 偶数轨深灰),并搭配addTrackStyleProvider在游玩中应用样式。
游玩引擎新能力(非 Mod API)
- 轨道 Z 层级:谱面额外内容新增
lane_z(真轨)与fake_track_z(假轨)特效,fake_track_create新增z字段;数值为任意整数,越大越靠上层。任一轨道 Z 非 0 时启用分层渲染路径(按 Z 升序逐单元绘制),全部为默认 0 时渲染顺序与旧版完全一致。注意:分层激活时真轨快捷键框随轨道层级绘制,打击特效 / combo 绘制于其上。 - 假轨道画布原点:
fake_track_create的originLane = -1表示以画布 (0,0) 为原点(offset 即画布坐标),不绑定真轨道、不跟随 track_move / lane_move;快捷键框显示?占位(可被模组样式覆盖)。 - 真轨流速调节:谱面额外内容新增
lane_scroll特效(targetSpeed倍率可负,支持 animated 缓动),与模组 API 共写每轨倍率。负流速下音符自画布下方升起、过判定线后向上飞离,HOLD 体 / 连接入 / 剔除窗口均已适配;判定为时间驱动,不受影响。 - 音符收集窗口动态化:收集窗口由启动时按全局流速一次性计算,改为每帧按所有轨道有效流速的最小绝对值动态计算(
(画布高 + 10 字符) / 最小有效流速,接近 0 按 1px/s 兜底,30s 上限)。修复慢速轨道(lane_scroll低倍率)音符不在轨道顶部出现、而是半途冒出的问题。 - 真轨高度事件
lane_height:谱面额外内容新增lane_height特效(laneHeightConfig:lane / targetHeight / auto / animated / easing / startHeight)。向轨道顶端方向延伸轨道图形与音符出现高度(px,纯视觉不影响判定);auto=true为自动高度,每帧按旋转与画布尺寸计算,保证旋转后的轨道顶端永不进入画布。收集窗口随之按延伸高度自动扩展,自动高度轨道音符从屏幕外进入。 - 音符出现改为位置式硬切:
lane_height延伸 / 旋转轨道在视觉顶端边缘处对谱面元素(音符 / HOLD 体 / Barrier)施加垂直于轨道方向的裁剪,音符自边缘浮现,不再出现"高于轨道"的悬浮感。剔除上界取画布顶与轨道顶端延伸中更靠上者——旋转轨道与画布顶的交点在轨道本地坐标下随角度上移(弦长拉伸),固定界会导致音符在轨道中途出现;此前的透明度渐入方案已移除(高速下不明显,且整段 HOLD 按顶端单点计算渐入会把长 HOLD 身体整段隐藏,被误认为普通音符)。 - 音量旋钮 / 音量键输入(桌面系统):键盘音量键此前对宿主不可见——系统把它路由为
WM_APPCOMMAND,AWT/JavaFX 均收不到,java.awt.event.KeyEvent也没有对应常量。现在游戏窗口聚焦时,宿主用一个 WH_KEYBOARD_LL 低级键盘钩子同时完成「音量抑制 + 输入采集」:吞掉音量增减消息(系统音量不变)并把按下/释放沿采集进队列,每帧排空合成为普通按键事件注入输入系统:游玩内InputAccess与全局GlobalKeyEvent均可见,键码为字面量175(音量加 / 旋钮正转)、174(音量减 / 旋钮反转)、173(静音,钩子放行保留系统静音切换,同时注入)。窗口失焦时不采集不吞,自动还原系统语义。非桌面系统平台无此输入。适合实现旋钮调速、旋钮切换轨道/菜单等连续量玩法。 - 布局网格与字体解耦:谱面几何(轨道区域 / 轨道宽 / 判定线 / 音符 / HOLD 体 / 快捷键框 / 假轨道 / 额外内容锚点)改为基于固定布局网格(10×19,与编辑器预览一致)计算,不再随运行时字体变化——修复了不同字体下轨道位置与编辑器预览不一致的问题(典型表现:
lane_rotate90° 后轨道水平错位)。字体仅影响字形渲染;HUD / 右侧状态面板等编辑器不复刻的 UI 仍随字体缩放。 - 设置场景集合化:设置场景支持集合面板(见上文 Mod API 小节);移除「增强视觉构成」开关及其对应渲染(中央指示线 / 判定线装饰 / 按键色派生),此类效果改由模组经轨道样式覆盖实现。
- 上述能力当时仅实现在 JavaFX 渲染路径(当时运行时),Swing 遗留渲染代码不涉及;渲染后端后续已迁移为 LWJGL3/OpenGL,本条为历史说明。
行为变化与迁移说明
- 「增强视觉构成」设置项已移除:依赖该开关的渲染(轨道中央线 / 判定线装饰 / 按键色派生)随之删除。既有谱面不含该字段不受影响;需要此类视觉效果的,改用
addTrackStyleProvider实现(参考mods-trackbg)。 - 模组设置项请使用
ctx.uiCustomization().settings(),与Mod.createConfigScene()(独立配置场景)按规模取舍。
0.7.3
新增十余项 Mod API(轨道输入映射、网络原语、文本输入与 IME、UI 定制、场景跳转拦截等);THS 支持块式 if 与 on_render 自绘;修复 JSON 解析与中文输入等多项缺陷。
- 发布日期:2026-08-30
- 对比基线:0.7.2(commit 4d0f6dc)
- 接口状态:全部 Mod API 仍标注
@Experimental(实验性,可能调整)
新增 Mod API
| 能力 | 入口 | 说明 |
|---|---|---|
| 轨道输入映射 | GameplayMod.createLaneInputMapper() | 接管「物理按键 → 逻辑轨道」映射。实现 LaneInputMapper 接口:init(laneCount) 在游玩开始时调用一次;mapKeyToLane(keyCode) 返回轨道号(返回 -1 表示不触发);mapKeyToLanes(keyCode) 支持一键触发多条轨道;triggerKeys() 声明所有可能触发判定的键。映射结果会再经过游戏模式的 mapLane(镜像翻转等仍然生效)。多模组按键级路由叠加:每个键由声明管辖它的第一个模组负责,不同模组管不同键即可共存 |
| 网络原语 | ctx.network().executor() | 获取本模组专属的调度线程池(核心 4 守护线程,命名 mod-net-<modId>-N)。宿主只托管线程生命周期,协议由模组用标准 java.net / java.nio 自行实现。模组停用或游戏退出时线程池被 shutdownNow(),之后调用 executor() 抛 IllegalStateException;阻塞 IO 禁止在 FX 线程执行,socket 须在 onUnload 自行关闭 |
| 文本输入组件 | com.seecmd.mod.api.text.TextField | 宿主与模组共用的单行文本框逻辑:光标移动、Shift 选区、Ctrl+C/X/V/Z/Y/A 剪贴板与撤销重做、上下键输入历史、长度 / 数字 / 字符过滤、密码掩码(display())、中文 IME。支持链式配置 password(bool)、enableHistory(n)、charFilter(pred);输入经 handleInput(InputEvent) 喂入,绘制用标准 render(canvas, x, y, w, h, focused)(深底边框、选区反白、光标、水平滚动) |
| IME 候选框锚点 | com.seecmd.mod.api.text.ImeAnchor | report(x, y) / clear() 上报输入框光标位置(1280×720 逻辑坐标),宿主据此把输入法候选窗定位到光标处。走 TextField.render() 时自动上报;完全自绘的场景需手动调用 |
| UI 定制 | ctx.uiCustomization() | 进程级能力,任意线程可调。见下方「UI 定制能力」 |
| 场景跳转拦截 | SceneHost.addTransitionInterceptor(...) | 见下方「场景跳转拦截」 |
| 确认弹窗 | Panels.confirm(title, lines, onResult) | 系统双按钮弹窗(确定 / 取消),Enter 确定与 Esc 取消。确定回调 accept(true),取消及任何其它关闭原因回调 accept(false),恰好回调一次,在游戏线程同步执行 |
| 弹窗关闭回调 | PanelHandle.setOnClosed(Runnable) | 面板因任何原因(点 X、close()、宿主清理)开始关闭时触发一次。常驻面板可借此自动重建;注意 onUnload 中主动 close() 也会触发,重建逻辑需要终止条件 |
| 谱面元数据视图 | GameplayContext.chartView() | 返回 ChartView:title() / author() / difficulty() / durationMs() / bpm() / totalNoteCount() / laneCount() / extensions()(谱面 JSON extensions 段只读视图)/ chartPath()(谱面主文件路径,可 null,可用于定位 <基名>_mods.json 等伴生文件) |
| 当前模式查询 | GameplayContext.currentModeId() | 返回当前游戏模式 id(如 seecmd:normal) |
| 谱面详情打开事件 | ChartDetailOpenedEvent | 玩家在选歌浏览中打开谱面详情面板时经全局事件总线发布,携带 laneCount() 与 chartTitle()。典型用途:轨道数超出模组支持范围时提前提示玩家 |
| 输入事件扩展 | InputEvent / InputType | InputType 新增 TEXT_INPUT(键入字符 / IME 提交文本,经 text() 携带);InputEvent 新增 text()、shiftDown()、ctrlDown() |
| 跨模组命名服务 | ctx.modServices() | 通用跨模组联动原语:模组把对外读写的状态/参数暴露为命名服务(全局 id <modId>:<name>),其它模组可按 id 查询并实时读写,无需编译期耦合。见下方「跨模组服务」小节 |
UI 定制能力
- 主界面状态栏自定义块:
addStatusBarItem(id, text)返回句柄,支持setText、setOnClick(回调在游戏线程执行)、remove。块以 bevel 按钮样式渲染在 ITEM 块右侧、按注册顺序排列,空间不足自动跳过;同 id 重复注册为幂等更新。 - 结算界面覆盖:
addResultOverlay(id, renderer),渲染器在原版结算内容之上绘制,入参为ResultSnapshot(谱面信息、分数、连击、准确率、评级、判定计数、自定义判定计数、新纪录标记等)与ResultLayout(画布与结果窗口矩形)。 - 生命周期:模组停用时宿主自动移除该模组全部定制项,无需在
onUnload手动清理。
场景跳转拦截
SceneTransitionInterceptor.intercept(from, to, kind, params)覆盖所有内核场景跳转(kind为PUSH/REPLACE/BACK,含原版界面之间)。- 决策:
Decision.proceed()放行、veto()静默取消、redirect(target[, params])重定向(新目标不再过拦截器,防环)。按模组激活顺序询问,第一个否决 / 重定向生效;回调抛异常按放行处理。 - 必须纯同步决策,回调内不得再触发跳转。标准用法:veto 后用
Panels.confirm弹窗征求确认,确认后重发跳转。
模组加载顺序
Mod.loadOrder()返回ModLoadOrder(stage, loadAfter, loadBefore),LoadStage分CORE/PRE_INIT/INIT/POST_INIT(默认INIT)。阶段决定模组激活顺序,进而决定游玩滤镜 / 背景 / 特效的叠加顺序(CORE 最底层)。loadAfter/loadBefore是软依赖(目标不存在自动失效);硬依赖仍用ModInfo.dependsOn()。- 防滥用:声明 CORE 但无人依赖它的模组自动降级 INIT 并告警。
跨模组服务(ModService / ModServiceRegistry)
- 提供方(如 Blur Cover 背景模组):在
onLoad经ctx.modServices().register("background", controller)暴露自己的控制面;全局 id 由宿主拼接为<本模组id>:<name>,例如blur_cover_bg:background。 - 消费方(任意其它模组):经
ctx.modServices().find("blur_cover_bg:background")按全局 id 查询,返回非 null 即可用统一原语读写目标服务的状态。为保证查询时服务已注册,消费方应在Mod.loadOrder()声明loadAfter目标模组 id。 - 服务提供统一的类型化读写原语(
boolValue/intValue/doubleValue/stringValue+ 对应set*),不预设业务含义;键的类型与语义由提供方约定。实现方保证读写线程安全,调用可发生在任意线程。 - 生命周期:模组停用 / 注销时宿主自动移除该模组注册的全部服务,无需在
onUnload手动清理;消费方持有的服务引用随之失效,运行时按find返回 null 处理。 - 参考实现:
blur-cover已升级为正式模组,mods-link-test提供消费方演示(游玩内按键实时调背景显隐/暗度/透明度/模糊)。
GameplayHud 扩展
- 新增
updateValues(HudValues):宿主每帧注入数值快照,与values()配对使用,HUD 模组应实现它保存快照(旧的自取方式仍可用,但不再保证时序)。 - 新增
wantsOverlayWithoutHiding():返回 true 时即使不隐藏任何原版 HUD 组件也调用renderOverlay,用于在原版 HUD 之上附加绘制(提示层 / 装饰层)。 - 多模组叠加:多个模组同时提供
createGameplayHud时不再只取第一个——所有实例按启用顺序收集,可见性声明取「任一隐藏即隐藏」(最严格),覆盖渲染依次调用、可互相叠加。
行为变化与迁移说明
GameModeDefinition.overridesJudgment()已废弃:判定级接管改由NoteTypeDefinition.overridesJudgment()+onJudgeTick(音符类型级逐帧判定)与onHandleInput(模式级输入消费)完成,宿主不再派发该值。- 模组场景现在接收全部按键按下事件(此前仅 UP/DOWN/LEFT/RIGHT/ENTER/ESC),并新增
TEXT_INPUT逐字符转发;事件缓冲保序,短于一帧的快速点击也会逐条到达。 SceneHost.push / replace / pop线程安全化:非 FX 线程调用自动封送到 FX 线程执行;pop()在无历史时静默忽略(修复模组场景长按 ESC 莫名退回主界面的问题)。JsonUtil.parseObject / parseArray现声明抛IOException(具体为新的JsonParseException,带行列号),不再抛裸ClassCastException/RuntimeException。直接调用这两个方法的模组需补 catch。- THS 内置判定短名化:
note_result由完整 id(seecmd:perfect)改为短名(perfect),自定义判定仍保留完整 id。旧脚本若比较完整内置 id 需要调整。 - 模组作用域注册表:
judgmentKinds()/gameModes()/noteTypes()/capabilities()/chartExtensions()/globalEventBus()返回绑定当前模组 id 的视图,注册与订阅自动归属;模组停用时宿主统一清理,不再残留泄漏。正常模组无感知。 - 模组场景注册:同 id 重复注册改为替换新实例(支持热重载),并提供
register(scene, modId)显式归属;模组停用时其场景被注销。
THS 语言新增
on_render事件:脚本声明后每帧触发,事件体内的text/rect/line绘制命令拿到真实画布执行,可实现常驻自绘 HUD。- 块式
if命令:if 条件 { ... }支持嵌套;条件支持字符串比较note_result == "perfect"、!=(旧版仅数字比较,字符串比较会解析崩溃)。 trigger/effect支持多行参数块:trigger "flash" { color = ... },单行内联与多行k = v两种形式均可。text命令支持"字符串" + 变量拼接与尾随 x y 坐标。- 头行内联
{(如on_combo 50 {、set_filter "glitch" {)不再解析失败。 - 新模板
strict:严格判定,参数perfect(默认 50ms)、great(默认 100ms)。注意判定窗口取多个 policy 的最大值,收紧窗口时需停用其它宽松判定模组。 - 修复
note_result字符串比较导致解析崩溃的问题;THSHudCombo 模板坐标改用 1280×720 常量并适配数值注入。
修复
- JSON 解析(
JsonUtil)多项缺陷:\uXXXX转义此前不被支持,现完整支持并校验;新增\b/\f转义;防御孤立反斜杠与截断的\u;key 后强制:校验;null/true/false字面量校验;数字支持科学计数法;所有错误统一为带行列号的JsonParseException。 scores.json顶层非法或条目损坏时跳过并告警,不再崩溃。_extra文件路径构造只去末尾扩展名,修复文件名中部含.json子串时被误删的问题;选歌列表不再显示_extra.json、_mods.json、folder_index等附属文件。- 场景切换时清空按键事件缓冲与鼠标按压沿,修复「点击打开模组场景后残留按压立即触发返回」的幽灵输入问题。
- 中文 IME 全面修复:画布注入输入法上下文,提交文本逐字符进入按键链路;多字符 KEY_TYPED 不再被截断(旧版「你好」只收到「你」);候选窗跟随
ImeAnchor定位;画布焦点跟随点击。 - 模组场景长按 ESC 自动重复不再触发「导航错误」弹回主界面(有历史才 back,每帧至多一次)。
- 场景创建失败时保持旧场景原状,不再白屏退出。
- 过期 Barrier 的批量 MISS 补发顺序修复(此前该逻辑实际不可达)。
- 多个右下角弹窗级联纵向排布,不再完全重叠;确认弹窗消费 Enter / Esc,不再漏给底层场景。
模组生态支撑(游戏本体)
- 自定义音符类型管线打通:宿主用原始音符 JSON 调用
NoteTypeDefinition.deserialize(rawJson, ctx)与onCreateRuntime(提供 totalLanes / bpm / contentScale),运行时数据存入NoteData.runtimeData;判定回调通过onJudged(HOLD 起止各一次)、onRemoved(完全判定时一次)与onJudgeTick拿到真实运行时对象。 - 自定义游戏模式生命周期补全:场景退出时调用当前模式的
onExit。 - 游玩滤镜下的系统 UI 保护:暂停对话框 / 读秒遮罩绘制在模组滤镜之上,不被覆盖。
- 新表演事件类型:
lane_note_alpha(真轨道音符透明度,不影响轨道背景)与lane_rotate(真轨道整体视觉旋转,圆心judge_line/track_center,支持动画);判定不受影响。 LaneView.angle()返回lane_rotate设置的当前旋转角度(x/y/w/h 保持未旋转布局矩形:旋转只以angle单列,lane 偏移与形变不烘焙进该矩形)。其中x()/y()为画布坐标——计入轨道区平移trackOffset(2.0 已修正,见 2.0「行为变化」的;本条发布时尚未定义trackOffset),width()为每轨宽、height()沿真轨/假轨各分支现状(2.0 已修正宽度口径,见 )。- 模组资源生命周期统一管理:停用 / 注销模组时清理事件订阅、能力、注册表条目、模组场景、状态栏块、跳转拦截器与网络线程池,不再泄漏 ClassLoader 与监听器。