Theseus 模组文档
更新日志

更新日志

模组系统的版本变更记录:新增 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.bat Step 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 原语(MatrixStack z 通道,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 图元不光栅化的问题(输出改独立缓冲,与 2D rotate 既有纪律同款;新增透视相机 + 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) + CollectItemonPreInit 内 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_rotate 90° 后轨道水平错位)。字体仅影响字形渲染;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.ImeAnchorreport(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 / InputTypeInputType 新增 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 与监听器。