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() | 把整張圖上傳 GPU。開銷不小——配合 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() | 釋放 GPU 記憶體。在 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 之上增加的能力。