JS 公共 API
四类脚本都能用的全局类与函数。表格按主题分组;各脚本类型专属的 ctx / wrapper 见 JS 脚本类型,晏阳自有的扩展见 晏阳扩展 API。
速记约定
static前缀 = 直接以类名调用(Resources.id(...));没有它就需要先拿到实例(通常来自其他函数的返回值)。- 类型标注按 Java 书写:
str: String收 Java 字符串(JS 字符串自动转换);返回String?表示可能为null;String...表示可变个数的字符串参数。 - 标 已弃用 的项是 NTE 时代的兼容接口,新脚本别再用;标 晏阳 的项是本分支自有扩展。
资源与文件
Identifier(资源路径)
Minecraft 用 命名空间:路径 形式的 Identifier 定位资源。自 JCM v2.3 起,大多数收 Identifier 的地方也能直接给普通字符串:完整形式 "mtr:asd.png",相对当前脚本目录的路径加 ./ 前缀(等价 idr())。
| 函数 | 说明 |
|---|---|
static Resources.id(idStr: String): Identifier | 由字符串创建 Identifier |
static Resources.idr(relPath: String): Identifier | 相对当前脚本所在目录解析;只能在解析阶段用(运行期没有「当前脚本」概念) |
static Resources.id(id: Identifier, suffix: String): Identifier | 在已有 Identifier 后追加后缀——解析期用 idr() 记下基准,运行期再拼路径 |
Resources(资源包读取)
从磁盘读资源比内存操作慢得多:能在解析阶段读好的内容别放进 render() 每帧重读。
| 函数 | 说明 |
|---|---|
static Resources.read(id): DataReader? | 读取资源包文件,返回 DataReader;文件不存在返回 null |
static Resources.exist(id): boolean | 文件是否存在 |
static Resources.getSystemFont(name: String): Font | 取 AWT 字体。内置项:Noto Serif(CJK 衬线)、Noto Sans SemiBold、Noto Sans CJK TC Medium;另收 Serif / SansSerif / Monospaced(依玩家机器已装字体而定) |
static Resources.hasSystemFont(name: String): boolean | 系统里是否存在该字体族(不做内置项特判) |
static Resources.getFontRenderContext(): FontRenderContext | AWT 文字度量用的上下文 |
static Resources.ensureStrFonts(text, primaryFont, fallbackFonts...): AttributedString | 按主字体+后备字体链组装文本,主字体缺字时自动回退——多语言混排防「豆腐块」用 |
static Resources.getAddonVersion(modId: String): String | 取模组版本:"mtr" / "jcm" / "mtrscripting"(车辆 / 装饰物 / 电梯脚本查 mtrscripting,PIDS 查 jcm) |
| 已弃用 | 说明 |
|---|---|
Resources.readString(id) / readBufferedImage(id) / readFont(id) | 统一改用 read() + DataReader |
Resources.manager() / Resources.mgr() | MTR 4 跨版本框架不暴露,恒返回 null;需要它的函数也接受 null |
Resources.getMTRVersion() 等 NTE 版本函数 | 改用 getAddonVersion();NTE 系列恒返回旧值只为兼容 |
Files(本地文件)
只能在 .minecraft 内活动:读 + 写 + 删限 .minecraft/data/mtrscripting(脚本数据目录),读放开到整个 .minecraft。路径以多个参数传递,不要带斜杠:Files.read("config", "mtr.json")。
| 函数 | 说明 |
|---|---|
static Files.read(path: String...): DataReader? | 读 .minecraft 下的文件,不存在返回 null |
static Files.readData(path: String...): DataReader? | 同上,但相对 data/mtrscripting |
static Files.saveData(content: String | BufferedImage, path: String...): void | 保存文本或图片;父目录自动创建 |
static Files.deleteData(path: String...): void | 删除文件 |
static Files.hasData(path: String...): boolean | 文件 / 目录是否存在 |
数学与变换
Vector3f(三维向量)
坐标 (x, y, z)。JCM 实现底层是 double、对外收放 float,运算方法都返回自身可链式调用:
new Vector3f(x, y, z)(也可由 TSC 的 Vector / Position 构造)· x() y() z() · copy() · normalize() · add(x,y,z) / add(v) · sub(v) · mul(x,y,z) / mul(n) · rotX(rad) / rotY(rad) / rotZ(rad)(弧度)· cross(v) · distance(v) · distanceSq(v) · rawBlockPos()。常量 Vector3f.ZERO / XP / YP / ZP——只读,别改动。
Matrices(变换栈)
渲染常需要「画完这一段、回到之前的变换」。Matrices 是一个变换栈,push 与 pop 成对使用:
new Matrices()(初始为单位矩阵)· translate(x,y,z) · rotateX/Y/Z(rad)(弧度)· rotateX/Y/ZDegrees(deg)(角度)· pushPose()(保存当前状态)· popPose()(恢复上次保存)· popPushPose()(先恢复再保存)。
渲染
脚本在后台线程跑,渲染在主线程——因此绘制走绘制指令(draw call)模式:你在 render() 里把「怎么画」提交给 RenderManager,JCM 记住这批指令,直到你下一次执行前一直按它画。旧指令在每次脚本执行后被清空,想持续显示就得每轮重新提交;但执行慢导致漏帧也不怕——JCM 会沿用上一批指令直到你交出新的。
RenderManager
由各类型 ctx.getRenderManager() 获得,自带一个「基础变换」(装饰物 = 方块位置 + GUI 位移与旋转;车辆按车厢……各类型见 脚本类型页),一切提交相对它绘制:
| 函数 | 说明 |
|---|---|
drawModel(model: Model, matrices?: Matrices) | 提交模型;可选附加变换 |
drawModel(model, matrices?, light: int) | 同上,自定义光照(MinecraftClient.lightAt 取值) |
drawModel(holder: DynamicModelHolder, matrices?: Matrices) | 直接提交动态模型持有器——内部自动处理「下一帧才传完」的空档,避免闪烁 |
draw(call: DrawCall) | 提交裸绘制指令(QuadDrawCall 等) |
QuadDrawCall(四边形)
画一个四边形面,可指定纹理、UV、染色、光照——常拿来贴动态纹理模拟屏幕:
static QuadDrawCall.create()(可带注释参数)· quad(c1, c2, c3, c4) 或 corner1..4(pos)(四个 Vector3f)· renderType(str) · color(rgb) · texture(id) · uv(u1,v1,u2,v2)(0–1)· matrices(m)。
renderType 取值:EXTERIOR(车外漆面,受位置光照,默认)、EXTERIOR_TRANSLUCENT、INTERIOR(车内,全亮带面着色)、INTERIOR_TRANSLUCENT、LIGHT(全亮无阴影,适合 LCD)、LIGHT_TRANSLUCENT。模型侧另有 ALWAYS_ON_LIGHT。
模型
支持 WaveFront OBJ 与 Metasequoia(.mqo);Blockbench 的 .bbmodel 不支持。Blender / Blockbench 导出的 OBJ 需要翻转 V 轴(flipV = true,默认值),Metasequoia 导出的要显式传 false。
| 类 | 要点 |
|---|---|
ModelManager | loadModel(id, flipV=true): RawModel、loadModelParts(id, flipV=true): Map<String, RawModel>(按对象组拆分)、upload(rawModel): Model(上传 GPU;仅解析阶段——运行期用 DynamicModelHolder) |
RawModel | 顶点数据容器:append(mesh|model)、applyTranslation(x,y,z)、applyRotation(dir, angleDeg)、applyScale(x,y,z)、applyMirror(x,y,z)、applyUVMirror(u,v)、setAllRenderType(str)、copy()、replaceTexture(fileName, id)、replaceAllTexture(id) |
Model | 已上传 GPU:顶点不可再改,但仍可 replaceTexture(...) / replaceAllTexture(...) / copyForMaterialChanges() / close() |
RawMeshBuilder | 代码建模型:new RawMeshBuilder(faceSize, renderType, textureId) 后 vertex(pos) → normal(x,y,z) → uv(u,v) → endVertex()(可加 color(r,g,b,a)),getMesh() 或 asRawModel() 收工 |
DynamicModelHolder | 运行期上传:uploadLater(rawModel)(下一帧在渲染线程上传)、getUploadedModel()(可能为旧模型或 null,优先直接把持有器交给 drawModel)、close() |
运行期的简单变换(平移 / 旋转 / 缩放)用 Matrices 传给 drawModel,别每帧改顶点再重上传——后者性能差几个数量级。
动态纹理
GraphicsTexture
固定分辨率的虚拟纹理 + Java AWT 画布,画完上传给游戏显示——车内外屏幕、LED 显示牌都靠它:
| 成员 | 说明 |
|---|---|
new GraphicsTexture(width, height) | 创建。逐块屏 / 逐车的内容请在 create 里建并存进 state |
.identifier | 该纹理的 Identifier——交给 QuadDrawCall.texture() 或替换模型贴图 |
.bufferedImage / .graphics | AWT BufferedImage 画布与其 Graphics2D(默认只开文字抗锯齿) |
.fullAA() / .noAA() | 全抗锯齿 / 全关(点阵屏常用 noAA) |
.upload() | 把整张图上传显存。开销不小——配合 RateLimit 降频(如 10 Hz)、远处不更新 |
.upload(x, y, width, height) | 只上传子区域,旧内容保留。参数形式 2:upload(dstX, dstY, srcX, srcY, w, h);还可直接传外部 BufferedImage:upload(img, dstX, dstY, srcX, srcY, w, h) |
.close() | 释放显存。在 create 里建就必须在 dispose 里关,否则泄漏 |
晏阳 .uploadRawABGR(bytes, dstX, dstY, w, h, lut) | 把 BGRA 顺序的字节行阵直写进纹理再只上传该矩形——供逐像素高频更新省带宽,详见 晏阳扩展 |
AWT 侧常用:Color.decode / new Color、graphics.setColor / setFont / setStroke、drawRect / fillRect / drawRoundRect / fillRoundRect、drawImage / drawString、Font.deriveFont、GradientPaint、AffineTransform、setClip、AlphaComposite;用 Rhino 的 importPackage(java.awt) 引入。教程看 Graphics2D 的 JavaDoc。
新建的纹理内容不保证干净(可能复用旧内存显示花屏)——创建后先 upload() 一次刷成自己的内容。
车辆多屏需求可改用内置的 DisplayHelper(include(Resources.id("mtrsteamloco:scripts/display_helper.js"))):一张大纹理切槽位、模型随配置自动生成,见 脚本类型页。
声音
SoundManager
各类型 ctx.getSoundManager() 获得,基准位置随类型而定:
playLocalSound(id, volume, pitch, category="MASTER")(无定位,报站类)· playSound(id, pos, volume, pitch, category="MASTER")(世界定位,相对基准位置)· play(soundInstance) / stop(soundInstance)(可控制实例)。
TickableSoundInstance
一次性音效播完就没法再管;需要中途调音量 / 打断的场景用可控制实例(车载复杂报站、电机音、循环音乐):
static create(id, category) · setSoundVolume(v) / setSoundPitch(v)(1.0 为基准)· setRepeatable(b) · setLoopDelay(tick) · setRelative(b)(跟随玩家恒定音量)· setPos(v)(循环声音必须自己逐帧更新位置)· isPlaying()。播放由 play() / play(delayTick) / play(ctx) 驱动,abort() 可中途切断(上游 v2.3.0-prerelease.3 重构)。循环实例务必在 dispose 里 stop。
已弃用:setLoopable(b)——v2.3.0-prerelease.3 重构后由 setRepeatable(b) 取代;既有脚本照常运行。
声音类别:MASTER BLOCKS MUSIC RECORDS WEATHER HOSTILE NEUTRAL PLAYERS AMBIENT VOICE。
网络与后台
Networking
static Networking.fetch(url, requestOption?): NetworkResponse<DataReader>。RequestOption 是普通 JS 对象:method(默认 GET)、connectTimeout / readTimeout(毫秒)、headers(对象)、body(字符串,自动带 Content-Length)。
NetworkResponse:getResponseCode() · success()(拿到响应即真——判业务成功用 ok(),即 200–299)· getData()(失败为 null)· exception() · getHeaders()。
请求默认带 User-Agent: Joban Client Mod (https://jcm.joban.org),正常途径改不掉(除非玩家关闭脚本限制)。网络请求放后台线程做(见下)。
BackgroundWorker
static BackgroundWorker.submit(task) 把 lambda 任务交给单线程后台执行器——别过度依赖,其他脚本也要用它;任务内部的异常要自己 try/catch。资源包重载时队列清空、在跑任务被中断,卡死也能自愈。
BackgroundWorker.submit(() => {
let resp = Networking.fetch("https://example.com/data.json");
if (resp.ok()) {
let str = resp.getData().asString();
// 处理后写进 state,render 里读
}
});
控制台
print(...) 与 console.log(...) 等价(NTE 遗产,随便用)。Console API 对齐 Web 惯例但不含表格 / profile 类方法:
console.log / warn / error / debug(...)(debug 仅调试模式输出)· console.count(label="default") / countReset(label)(计数器按标签独立)· console.time(label) / timeLog(label) / timeEnd(label)(计时器按标签独立)。占位符插值不支持,模板字符串够用。
MTR 数据
MTR.ClientConfig(玩家偏好)
isChatAnnouncementEnabled() · isTtsAnnouncementEnabled() · shouldHideTranslucentParts() · getLanguageDisplay()(NORMAL / CJK_ONLY / NON_CJK_ONLY)· getDynamicTextureResolution()(0–8)· getVehicleOscillationMultiplier()(×10 得百分比)· getDefaultRail3D() · isCustomFontEnabled()。
MTR.Data(客户端已知数据)
MTR 4 只同步附近的数据——查不到远处不奇怪,很多函数会返回 null:
| 分组 | 函数 |
|---|---|
| 车站 / 车厂 | findStation(pos) · getStation(id) · getKnownStations();findDepot(pos) · getDepot(id) · getKnownDepots() |
| 线路 | getRoute(id) · getKnownRoutes() · getKnownRoutesPassingPlatform(platformId) |
| 载具 | getVehicle(vehicleId) · getPlayerMountedVehicle() · getKnownVehicles() · isPlayerMounted(id) · getVehicleMountDetail(): MountDetail(ridingCarIndex(): int、相对车厢中心的 pos(): Vector3f,上游 v2.3.0-prerelease.3) |
| 电梯 | getLift(id) · getPlayerMountedLift() · getKnownLifts() · getLiftFloors(tscLift): List<Floor>(TSC 电梯的楼层表,上游 v2.3.0-prerelease.3) |
| 轨道 | getRailFromPath(pathData) · getKnownRails() · isRailBlocked(hexId) · getKnownBlockedRails() · getBlockedSignalColors(hexId) · getConnectedRails(pathData, front, back, excludeSelf = false) / getConnectedRails(pos)(与路径端点 / 轨道节点位置相连的轨道,上游 v2.3.0-prerelease.3) |
| 站台 / 侧线 | getPlatform(id) · getKnownPlatforms() · findNearbyPlatforms(pos, radius);getSiding(id) · getKnownSidings() · findNearbySidings(pos, radius) |
| 到站 | getArrivals(platformId | platformIds[]): ArrivalEntries——与 PIDS 脚本里 pids.arrivals() 同物 |
MTRClientData 是 MTR 内部数据类的直通引用,仅作最后手段——它没有稳定性承诺,跨版本可能变。
Minecraft 环境
MinecraftClient
worldIsRaining() · worldIsThundering() · worldIsRainingAt(pos) · worldDayTime() · blockLightAt(pos) · skyLightAt(pos) · lightAt(pos)(渲染用打包值)· lightLevelAt(pos) · renderDistance() · localPlayer() · gamePaused()(TSC 模拟不随暂停停!)· narrate(msg) · displayMessage(msg, actionBar)(也收 VanillaText)· spawnParticleInWorld(id, pos, velocity, alwaysSpawn=false) · getScoreboardScore(objective, player) · getWorldPlayers() · getCurrentWorldId() · getCameraMode() · getRedstoneLevel(pos) · getWorldBlockState(pos, stateName)。
VanillaText(原版富文本)
构造聊天 / 覆盖层用的原版文本组件:literal(str) · translatable(key, ...) · append(other) · withBold() · withItalic() · withFont(id) · withColor(rgb | name) · clickToCopyText(str) · clickToOpenURL(url) · withHoverText(str | VanillaText) · getString()(拿本地化结果)。
VoxelShape(方块轮廓)
装饰物自定义碰撞 / 轮廓用:empty() · fullCube() · create(minX, minY, minZ, maxX, maxY, maxZ, facing?)(单位 16 = 一格)· combine(other);配合 ctx.setOutlineShape(...) / ctx.setCollisionShape(...)。
ItemStack / PlayerEntity
ItemStack:itemId() · translationId() · empty() · count()。PlayerEntity(通常来自 MinecraftClient.localPlayer() 或事件):uuid() · pos() / blockPos() / smoothPos() / velocity() · pitch() / yaw() / bodyYaw() · playerName() / displayName() · hasPermissionLevel(level)(客户端信息,别当真)· mainHandItem() / offHandItem() / activeItem() / isHoldingItem(id) · isSneaking() / isSprinting() / isSwimming() / isCreative() / isSpectator()。
工具类
DataReader(统一解码)
Resources.read / Files.read / Networking.fetch 都返回它——字节序列的解码器:
asString(): String?(UTF-8)· asBufferedImage(): BufferedImage? · asFont(): Font?(TTF/OTF;实现会在临时目录落文件,磁盘紧张可清 +~JF 开头的残留)· asByteArray(): byte[] · openInputStream(callback)(自动关流)· asInputStream()(要自己关)。解码失败返回 null 并在控制台报错。
其余工具
| 类 | 要点 |
|---|---|
Timing | elapsed()(游戏运行秒数,暂停也走)· delta()(距上次 render 的秒差——按帧率缩放动画全靠它)· currentTimeMillis() · nanoTime() |
TextUtil | 拆 MTR 的 中文|English||附加 命名格式:getCjkParts / getNonCjkParts / getExtraParts / getNonExtraParts / getNonCjkAndExtraParts / isCjk / cycleString(src[, tick]) |
StateTracker | 状态变迁跟踪(避免「满足条件就每帧播一次」):setState(value): boolean(变化才真)· stateNow() / stateLast() / stateNowDuration() / stateNowFirst() · changedTo(v) / changedFromTo(old, v);逐对象一份,存 state |
CycleTracker | 按时间循环轮播:new CycleTracker(["route", 5, "next", 5]) + tick() · stateNow() / stateLast() / stateNowDuration() / stateNowFirst() |
RateLimit | 限频:new RateLimit(intervalSec) + if (rl.shouldUpdate()) { ... };resetCoolDown() 立即到点 |
Java | Java 互操作帮手:Java.newArray(class, length | dims)、Java.toArray(class, list)——AWT 某些方法要真 Java 数组 |
脚本特性
include(脚本互相包含)
include(Resources.id("mtrsteamloco:scripts/display_helper.js")); // 绝对路径
include("./util/common.js"); // 相对当前脚本
在解析阶段加载并执行另一个 JS 文件(可收字符串相对路径或 Identifier)。
脚本条目互引
同包内没有内容引用的脚本条目默认拒绝加载(防漏写 scriptId);给脚本条目加 "forceLoad": true 可强制加载——跨包共享脚本条目、MTR 3 条目借 MTR 4 共享声明时用,注意资源包加载顺序(声明方先加载)。
ANTE 兼容类
SoundHelper(ANTE 风格放音)、TickableSound、GlobalRegister、ComponentUtil(原版文本组件工具)与全局 asJavaArray(list, class) 自 v2.3 起可用,签名与 ANTE 一致,方便 ANTE 内容迁移。
接下来:JS 脚本类型 —— 车辆 / 装饰物 / 电梯 / PIDS 的专属 API;晏阳扩展 API —— 本分支在 JS 之上增加的能力。