JS Common APIs

Notation

  • A static prefix = call it straight off the class name (Resources.id(...)); without it you first need an instance (usually obtained from another function's return value).
  • Type annotations follow Java notation: str: String takes a Java string (JS strings convert automatically); a String? return may be null; String... means a variable number of string arguments.
  • Items marked Deprecated are NTE-era compatibility interfaces — don't use them in new scripts; items marked Yanyang are this fork's own extensions.

Resources and files

Identifier (resource paths)

Minecraft locates resources with Identifiers of the form namespace:path. Since JCM v2.3, most places that take an Identifier also accept a plain string: the full form "mtr:asd.png", or a path relative to the current script's directory with a ./ prefix (equivalent to idr()).

FunctionDescription
static Resources.id(idStr: String): IdentifierCreates an Identifier from a string
static Resources.idr(relPath: String): IdentifierResolves relative to the directory of the current script; usable only at parse time (at runtime there is no notion of a "current script")
static Resources.id(id: Identifier, suffix: String): IdentifierAppends a suffix to an existing Identifier — record the base with idr() at parse time, then assemble the path at runtime

Resources (resource pack reads)

Reading resources from disk is far slower than in-memory work: whatever can be read at parse time shouldn't be re-read inside render() every frame.

FunctionDescription
static Resources.read(id): DataReader?Reads a resource pack file, returning a DataReader; returns null if the file doesn't exist
static Resources.exist(id): booleanWhether the file exists
static Resources.getSystemFont(name: String): FontFetches an AWT font. Built-ins: Noto Serif (a CJK serif), Noto Sans SemiBold, Noto Sans CJK TC Medium; also accepts Serif / SansSerif / Monospaced (depending on the fonts installed on the player's machine)
static Resources.hasSystemFont(name: String): booleanWhether the machine has that font family (no special-casing of the built-ins)
static Resources.getFontRenderContext(): FontRenderContextThe context used for AWT text metrics
static Resources.ensureStrFonts(text, primaryFont, fallbackFonts...): AttributedStringAssembles text with a primary font plus a fallback font chain, falling back automatically when the primary font lacks a glyph — the tool for multilingual mixed text without tofu boxes
static Resources.getAddonVersion(modId: String): StringGets a mod's version: "mtr" / "jcm" / "mtrscripting" (vehicle / decoration object / lift scripts query mtrscripting; PIDS queries jcm)
DeprecatedDescription
Resources.readString(id) / readBufferedImage(id) / readFont(id)Replace uniformly with read() + DataReader
Resources.manager() / Resources.mgr()Not exposed by MTR 4's cross-version framework; always returns null — and the functions that need it accept null too
Resources.getMTRVersion() and the other NTE version functionsUse getAddonVersion() instead; the NTE family keeps returning the old values purely for compatibility

Files (local files)

Confined to .minecraft: read + write + delete are limited to .minecraft/data/mtrscripting (the script data directory), while read is open to all of .minecraft. Pass paths as multiple arguments, with no slashes: Files.read("config", "mtr.json").

FunctionDescription
static Files.read(path: String...): DataReader?Reads a file under .minecraft; returns null if it doesn't exist
static Files.readData(path: String...): DataReader?Same as above, but relative to data/mtrscripting
static Files.saveData(content: String | BufferedImage, path: String...): voidSaves text or an image; parent directories are created automatically
static Files.deleteData(path: String...): voidDeletes a file
static Files.hasData(path: String...): booleanWhether a file / directory exists

Math and transforms

Vector3f (3D vectors)

Coordinates (x, y, z). The JCM implementation stores doubles internally while taking and giving floats externally, and every operation method returns the vector itself for chaining:

new Vector3f(x, y, z) (can also be constructed from TSC's 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) (radians) · cross(v) · distance(v) · distanceSq(v) · rawBlockPos(). The constants Vector3f.ZERO / XP / YP / ZP are read-only — don't modify them.

Matrices (transform stacks)

Rendering often needs "finish this stretch, then go back to the previous transform". Matrices is a transform stack; use push and pop in pairs:

new Matrices() (starts as the identity matrix) · translate(x,y,z) · rotateX/Y/Z(rad) (radians) · rotateX/Y/ZDegrees(deg) (degrees) · pushPose() (saves the current state) · popPose() (restores the last save) · popPushPose() (pops, then pushes).

Rendering

Scripts run on a background thread while rendering happens on the main thread — hence drawing follows the draw call model: inside render() you submit what to draw to the RenderManager, and JCM remembers that batch of draw calls and keeps drawing from it until your next execution. The old draw calls are cleared after each script execution, so anything that should stay visible must be re-submitted every round; but if slow execution makes you miss frames, don't worry — JCM keeps replaying the previous batch until you hand over a new one.

RenderManager

Obtained from each type's ctx.getRenderManager(); it carries a base transform (decoration objects = block position + GUI offset and rotation; vehicles = per car… — each type is covered on the script types page), and every submission draws relative to it:

FunctionDescription
drawModel(model: Model, matrices?: Matrices)Submits a model; an optional extra transform
drawModel(model, matrices?, light: int)Same as above, with custom lighting (values from MinecraftClient.lightAt)
drawModel(holder: DynamicModelHolder, matrices?: Matrices)Submits a dynamic model holder directly — internally bridges the "uploaded a frame later" gap, avoiding flicker
draw(call: DrawCall)Submits a raw draw call (QuadDrawCall and friends)

QuadDrawCall (quads)

Draws a quad face with optional texture, UVs, tint, and lighting — often used to blit a dynamic texture as a screen:

static QuadDrawCall.create() (optionally takes a comment argument) · quad(c1, c2, c3, c4) or corner1..4(pos) (four Vector3fs) · renderType(str) · color(rgb) · texture(id) · uv(u1,v1,u2,v2) (0–1) · matrices(m).

renderType values: EXTERIOR (the exterior paintwork, affected by positional lighting; the default), EXTERIOR_TRANSLUCENT, INTERIOR (the car interior, full-bright with face tinting), INTERIOR_TRANSLUCENT, LIGHT (full-bright with no shading — suited to LCDs), LIGHT_TRANSLUCENT. The model side additionally has ALWAYS_ON_LIGHT.

Models

Supports WaveFront OBJ and Metasequoia (.mqo); Blockbench's .bbmodel is not supported. OBJ exported from Blender / Blockbench needs the V axis flipped (flipV = true, the default), while Metasequoia exports need false passed explicitly.

ClassKey points
ModelManagerloadModel(id, flipV=true): RawModel, loadModelParts(id, flipV=true): Map<String, RawModel> (splits by object group), upload(rawModel): Model (uploads to the GPU; parse stage only — at runtime use DynamicModelHolder)
RawModelVertex-data container: 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)
ModelAlready on the GPU: vertices can no longer be edited, but replaceTexture(...) / replaceAllTexture(...) / copyForMaterialChanges() / close() still work
RawMeshBuilderBuild models in code: after new RawMeshBuilder(faceSize, renderType, textureId), go vertex(pos) → normal(x,y,z) → uv(u,v) → endVertex() (optionally add color(r,g,b,a)), then finish with getMesh() or asRawModel()
DynamicModelHolderRuntime uploads: uploadLater(rawModel) (uploads on the render thread next frame), getUploadedModel() (may be the old model or null — prefer handing the holder straight to drawModel), close()
Tip

For runtime transforms (translate / rotate / scale), pass a Matrices to drawModel instead of editing vertices and re-uploading every frame — the latter is orders of magnitude slower.

Dynamic textures

GraphicsTexture

A fixed-resolution virtual texture plus a Java AWT canvas, uploaded for the game to display once you're done drawing — in-car and exterior screens and LED signs all rely on it:

MemberDescription
new GraphicsTexture(width, height)Creates one. Per-screen / per-train content should be created in create and stored in state
.identifierThe texture's Identifier — hand it to QuadDrawCall.texture() or use it to swap a model's texture
.bufferedImage / .graphicsThe AWT BufferedImage canvas and its Graphics2D (text antialiasing on by default, nothing else)
.fullAA() / .noAA()All antialiasing / none (dot-matrix screens usually want noAA)
.upload()Uploads the whole image to VRAM. Not cheap — throttle with RateLimit (e.g. 10 Hz) and skip updates when far away
.upload(x, y, width, height)Uploads only a sub-region, keeping the old content. Second parameter form: upload(dstX, dstY, srcX, srcY, w, h); you can also pass an external BufferedImage directly: upload(img, dstX, dstY, srcX, srcY, w, h)
.close()Frees VRAM. What you create in create you must close in dispose, or it leaks
Yanyang .uploadRawABGR(bytes, dstX, dstY, w, h, lut)Writes a BGRA-ordered array of byte rows straight into the texture and uploads only that rectangle — a bandwidth-saving channel for high-frequency per-pixel updates; see Yanyang extensions

Common on the AWT side: Color.decode / new Color, graphics.setColor / setFont / setStroke, drawRect / fillRect / drawRoundRect / fillRoundRect, drawImage / drawString, Font.deriveFont, GradientPaint, AffineTransform, setClip, AlphaComposite; bring them in with Rhino's importPackage(java.awt). For a tutorial, see the Graphics2D JavaDoc.

Note

A freshly created texture's contents aren't guaranteed clean (reused memory may show garbage) — call upload() once after creation to flush in your own content.

For vehicles needing many screens, switch to the built-in DisplayHelper (include(Resources.id("mtrsteamloco:scripts/display_helper.js"))): one big texture sliced into slots, with the model generated automatically from the config — see the script types page.

Sound

SoundManager

Obtained from each type's ctx.getSoundManager(); the base position depends on the type:

playLocalSound(id, volume, pitch, category="MASTER") (no spatialization; announcements and the like) · playSound(id, pos, volume, pitch, category="MASTER") (positioned in the world, relative to the base position) · play(soundInstance) / stop(soundInstance) (controllable instances).

TickableSoundInstance

One-shot sounds can't be managed once they start playing; use controllable instances when you need to adjust the volume or interrupt mid-play (complex on-board announcements, motor sounds, looping music):

static create(id, category) · setSoundVolume(v) / setSoundPitch(v) (1.0 is the baseline) · setRepeatable(b) · setLoopDelay(tick) · setRelative(b) (follows the player at constant volume) · setPos(v) (looping sounds must update their own position every frame) · isPlaying(). Playback is driven by play() / play(delayTick) / play(ctx), with abort() to cut a playing instance short (reworked in upstream v2.3.0-prerelease.3). Always stop looping instances in dispose.

Deprecated: setLoopable(b) — superseded by setRepeatable(b) in the v2.3.0-prerelease.3 rework; existing scripts keep working.

Sound categories: MASTER BLOCKS MUSIC RECORDS WEATHER HOSTILE NEUTRAL PLAYERS AMBIENT VOICE.

Networking and background work

Networking

static Networking.fetch(url, requestOption?): NetworkResponse<DataReader>. RequestOption is a plain JS object: method (GET by default), connectTimeout / readTimeout (milliseconds), headers (an object), body (a string; Content-Length is added automatically).

NetworkResponse: getResponseCode() · success() (true once any response has arrived — to check business success use ok(), i.e. 200–299) · getData() (null on failure) · exception() · getHeaders().

Requests carry User-Agent: Joban Client Mod (https://jcm.joban.org) by default, and there's no regular way to change that (unless the player disables the script restrictions). Do network requests on a background thread (see below).

BackgroundWorker

static BackgroundWorker.submit(task) hands a lambda task to a single-threaded background executor — don't lean on it too hard, other scripts need it too; catch exceptions inside your task yourself. On resource pack reloads the queue is emptied and running tasks are interrupted, and it self-heals if it deadlocks.

BackgroundWorker.submit(() => {
    let resp = Networking.fetch("https://example.com/data.json");
    if (resp.ok()) {
        let str = resp.getData().asString();
        // Process it, store it in state, and read it from render
    }
});

Console

print(...) and console.log(...) are equivalent (an NTE legacy — use either freely). The Console API mirrors web conventions but omits the table / profile methods:

console.log / warn / error / debug(...) (debug only prints in debug mode) · console.count(label="default") / countReset(label) (counters are independent per label) · console.time(label) / timeLog(label) / timeEnd(label) (timers are independent per label). Placeholder interpolation isn't supported; template strings are enough.

MTR data

MTR.ClientConfig (player preferences)

isChatAnnouncementEnabled() · isTtsAnnouncementEnabled() · shouldHideTranslucentParts() · getLanguageDisplay() (NORMAL / CJK_ONLY / NON_CJK_ONLY) · getDynamicTextureResolution() (0–8) · getVehicleOscillationMultiplier() (×10 for a percentage) · getDefaultRail3D() · isCustomFontEnabled().

MTR.Data (data known to the client)

MTR 4 only syncs nearby data — it's normal not to find far-away data, and many functions return null:

GroupFunctions
Stations / depotsfindStation(pos) · getStation(id) · getKnownStations(); findDepot(pos) · getDepot(id) · getKnownDepots()
RoutesgetRoute(id) · getKnownRoutes() · getKnownRoutesPassingPlatform(platformId)
VehiclesgetVehicle(vehicleId) · getPlayerMountedVehicle() · getKnownVehicles() · isPlayerMounted(id) · getVehicleMountDetail(): MountDetail (ridingCarIndex(): int, pos(): Vector3f relative to the car centre — upstream v2.3.0-prerelease.3)
LiftsgetLift(id) · getPlayerMountedLift() · getKnownLifts() · getLiftFloors(tscLift): List<Floor> (the floors of a TSC lift — upstream v2.3.0-prerelease.3)
RailsgetRailFromPath(pathData) · getKnownRails() · isRailBlocked(hexId) · getKnownBlockedRails() · getBlockedSignalColors(hexId) · getConnectedRails(pathData, front, back, excludeSelf = false) / getConnectedRails(pos) (the rails connected to a path end / to a rail node position — upstream v2.3.0-prerelease.3)
Platforms / sidingsgetPlatform(id) · getKnownPlatforms() · findNearbyPlatforms(pos, radius); getSiding(id) · getKnownSidings() · findNearbySidings(pos, radius)
ArrivalsgetArrivals(platformId | platformIds[]): ArrivalEntries — the same object as pids.arrivals() in PIDS scripts

MTRClientData is a direct pass-through reference to MTR's internal data class; treat it as a last resort — it carries no stability promise and may change across versions.

The Minecraft environment

MinecraftClient

worldIsRaining() · worldIsThundering() · worldIsRainingAt(pos) · worldDayTime() · blockLightAt(pos) · skyLightAt(pos) · lightAt(pos) (the packed value used for rendering) · lightLevelAt(pos) · renderDistance() · localPlayer() · gamePaused() (TSC simulation keeps running through pauses!) · narrate(msg) · displayMessage(msg, actionBar) (also accepts VanillaText) · spawnParticleInWorld(id, pos, velocity, alwaysSpawn=false) · getScoreboardScore(objective, player) · getWorldPlayers() · getCurrentWorldId() · getCameraMode() · getRedstoneLevel(pos) · getWorldBlockState(pos, stateName).

VanillaText (vanilla rich text)

Builds the vanilla text components used in chat / overlays: literal(str) · translatable(key, ...) · append(other) · withBold() · withItalic() · withFont(id) · withColor(rgb | name) · clickToCopyText(str) · clickToOpenURL(url) · withHoverText(str | VanillaText) · getString() (returns the localized result).

VoxelShape (block outlines)

For custom decoration object collision / outlines: empty() · fullCube() · create(minX, minY, minZ, maxX, maxY, maxZ, facing?) (16 units = one block) · combine(other); pair with ctx.setOutlineShape(...) / ctx.setCollisionShape(...).

ItemStack / PlayerEntity

ItemStack: itemId() · translationId() · empty() · count(). PlayerEntity (usually from MinecraftClient.localPlayer() or events): uuid() · pos() / blockPos() / smoothPos() / velocity() · pitch() / yaw() / bodyYaw() · playerName() / displayName() · hasPermissionLevel(level) (client-side info — take it with a grain of salt) · mainHandItem() / offHandItem() / activeItem() / isHoldingItem(id) · isSneaking() / isSprinting() / isSwimming() / isCreative() / isSpectator().

Utilities

DataReader (unified decoding)

Returned by Resources.read / Files.read / Networking.fetch — a decoder for byte sequences:

asString(): String? (UTF-8) · asBufferedImage(): BufferedImage? · asFont(): Font? (TTF/OTF; the implementation drops files in the temp directory — clear the leftovers starting with +~JF if disk space is tight) · asByteArray(): byte[] · openInputStream(callback) (closes the stream automatically) · asInputStream() (you close it yourself). Failed decodes return null and log an error to the console.

Other utilities

ClassKey points
Timingelapsed() (seconds of game runtime, keeps counting while paused) · delta() (seconds since the last render — the tool for scaling animation to frame rate) · currentTimeMillis() · nanoTime()
TextUtilSplits MTR's 中文|English||附加 naming format (CJK | English || extra): getCjkParts / getNonCjkParts / getExtraParts / getNonExtraParts / getNonCjkAndExtraParts / isCjk / cycleString(src[, tick])
StateTrackerState transition tracking (avoids "fire every frame while the condition holds"): setState(value): boolean (true only on change) · stateNow() / stateLast() / stateNowDuration() / stateNowFirst() · changedTo(v) / changedFromTo(old, v); one per object, stored in state
CycleTrackerTimed carousel rotation: new CycleTracker(["route", 5, "next", 5]) + tick() · stateNow() / stateLast() / stateNowDuration() / stateNowFirst()
RateLimitRate limiting: new RateLimit(intervalSec) + if (rl.shouldUpdate()) { ... }; resetCoolDown() makes it due immediately
JavaJava interop helpers: Java.newArray(class, length | dims), Java.toArray(class, list) — some AWT methods want real Java arrays

Script features

include (scripts including scripts)

include(Resources.id("mtrsteamloco:scripts/display_helper.js"));  // Absolute path
include("./util/common.js");                                     // Relative to the current script

Loads and executes another JS file at parse time (accepts a relative path string or an Identifier).

Script entries referencing script entries

A script entry that no content entry in the same pack references is refused by default (guards against a forgotten scriptId); adding "forceLoad": true to the script entry forces it to load — used when sharing script entries across packs, or when an MTR 3 entry borrows an MTR 4 shared declaration; mind resource pack load order (the declaring pack loads first).

ANTE compatibility classes

SoundHelper (ANTE-style sound playback), TickableSound, GlobalRegister, ComponentUtil (vanilla text component utilities), and the global asJavaArray(list, class) are available since v2.3, with signatures matching ANTE to ease migrating ANTE content.


Next up: JS Script Types — the dedicated APIs for vehicle / decoration object / lift / PIDS; Yanyang Extensions — what this fork adds on top of JS.