iridium-script

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

简介

Introduction

Iridium 是一个 Minecraft 1.20.1/1.21.10 客户端,基于Forge运行,带有一个基于Java Script的脚本执行器
脚本可以注册模块、设置项、HUD 元素和聊天命令,监听游戏事件,直接调用 Java API。
Iridium is a Minecraft 1.20.1/1.21.10 client running on Forge, with a JavaScript-based script executor.
Scripts can register modules, settings, HUD elements, and chat commands, listen to game events, and directly call Java APIs.

开始之前

Before You Start

确保以下材料已经获得:
  • types.d.ts
    : 所有脚本API、Minecraft API大致定义,大部分为Minecraft API,你需要至少阅读Client API章节,Minecraft Types章节供速查
  • IridiumMCP
    : 提供一些工具帮助你开发
必须先咨询用户(如果用户未提供)写的脚本需要哪个版本,根据不同的版本下载types.d.ts,了解对应的Minecraft API
如果用户要求两个版本都兼容则使用
client.getMinecraftVersion()
来判断版本,执行对应的逻辑。
所有的API定义都在
types.d.ts
务必看
文件位置:
用途路径
脚本
%APPDATA%\scripts\*.js
本地库
%APPDATA%\scripts\.libs\*.js
Make sure you have the following resources:
  • types.d.ts
    : General definitions for all script APIs and Minecraft APIs, most of which are Minecraft APIs. You need to at least read the Client API chapter, and the Minecraft Types chapter is for quick reference.
  • IridiumMCP
    : Provides tools to assist you with development.
You must first consult the user (if not provided) which version the script is intended for, download the corresponding
types.d.ts
according to the version, and familiarize yourself with the corresponding Minecraft API.
If the user requires compatibility with both versions, use
client.getMinecraftVersion()
to determine the version and execute the corresponding logic.
All API definitions are in
types.d.ts
— be sure to read it.
File locations:
PurposePath
Scripts
%APPDATA%\scripts\*.js
Local libraries
%APPDATA%\scripts\.libs\*.js

MCP

MCP

MCP 提供 Harness 和客户端交互的接口,对开发有很大帮助。
即使客户端没打开,MCP也可以连接因为MCP和客户端是分离的,你可以通过
ping
工具确保客户端在线。
如果你无法使用IridiumMCP,按照以下步骤安装或者排查
MCP provides an interface for Harness to interact with the client, which is very helpful for development.
Even if the client is not open, MCP can still connect because MCP and the client are separate. You can use the
ping
tool to ensure the client is online.
If you cannot use IridiumMCP, follow the steps below to install it or troubleshoot:

未安装MCP

MCP Not Installed

下载
https://iridium.styles.wtf/assets/IridiumMCP.exe
到当前目录,不要放在临时目录不然下次可能找不到
根据任意方式配置,添加到你的harness mcp配置里,并且尽可能自主完成配置,如果有权限问题等实在无法自主完成的事情再找用户操作。
安装完毕之后用下方文案告知用户(根据用户的提示词语言翻译):
┌─ IridiumMCP Server Setup Complete ────────────────────┐
│  ✓ Config  <path>                                    │
│  ✓ MCPs    <path>                                    │
│                                                      │
│  ⚡ Restart your agent to load the MCP servers       │
└──────────────────────────────────────────────────────┘
Download
https://iridium.styles.wtf/assets/IridiumMCP.exe
to the current directory. Do not place it in a temporary directory, otherwise it may not be found next time.
Configure it in any way, add it to your harness MCP configuration, and complete the configuration on your own as much as possible. Only ask the user for operations if you really cannot complete them independently, such as permission issues.
After installation, inform the user with the following message (translate according to the language of the user's prompt):
┌─ IridiumMCP Server Setup Complete ────────────────────┐
│  ✓ Config  <path>                                    │
│  ✓ MCPs    <path>                                    │
│                                                      │
│  ⚡ Restart your agent to load the MCP servers       │
└──────────────────────────────────────────────────────┘

StreamHTTP

StreamHTTP

MCP会打开在
http://127.0.0.1:6767/mcp
如果
6767
端口被占用应使用
IridiumMCP.exe -http 127.0.0.1:6768
MCP will run at
http://127.0.0.1:6767/mcp
If port
6767
is occupied, use:
IridiumMCP.exe -http 127.0.0.1:6768

stdio

stdio

IridiumMCP.exe -stdio
IridiumMCP.exe -stdio

无法使用MCP

MCP Not Working

  • 先判断是不是HTTP URL写错了,正确的URL应该包含
    /mcp
    ,如果不包含
    /mcp
    就是错误的
  • 重点检查Claude Code,Open Code等工具的settings代理配置,是否忘记配置NO_PROXY,放行127.0.0.1
  • First check if the HTTP URL is written incorrectly. The correct URL should include
    /mcp
    ; if it does not include
    /mcp
    , it is wrong.
  • Focus on checking the proxy settings in tools like Claude Code, Open Code, etc. — whether you forgot to configure NO_PROXY to allow
    127.0.0.1
    .

库

Libraries

先读取
reference/library.md
有一些预制的库目录,如果你认为用户的诉求可能需要这个库,使用
get_library
工具获得源码以便确认用法,然后用
client.import
来导入,示例:
js
var clip = client.import("cubk/clipboard");
log("import ok, keys: " + Object.keys(clip).join(", "));
log("write: " + clip.write("MARKER-8964"));
First read
reference/library.md
which contains a directory of pre-built libraries. If you think the user's request may require a library, use the
get_library
tool to obtain the source code to confirm usage, then import it with
client.import
. Example:
js
var clip = client.import("cubk/clipboard");
log("import ok, keys: " + Object.keys(clip).join(", "));
log("write: " + clip.write("MARKER-8964"));

开发流程

Development Workflow

  1. 理解用户诉求,创建,还是修改现有脚本
  2. 尝试连接MCP,并使用
    ping
    工具确保客户端在线,如果客户端不在线告知用户需要启动客户端才能使用MCP,当然没有MCP也不是不能写,只是调试会很难
  3. 阅读
    types.d.ts
    理解客户端API
  4. 找到文件目录,寻找用户要修改的脚本或者创建脚本,可以使用MCP
    list_scripts
    工具快速查看
  5. 编写测试版脚本,确保所有逻辑跑通,期间可以通过
    eval
    ,
    get_errors
    ,
    grep_logs
    ,
    get_logs
    工具调试,使用
    log()
    接口输出的的日志将可以通过
    grep_logs
    和
    get_logs
    获得,优先使用
    grep_logs
    来节省上下文
  6. 如果用户编写的是视觉功能,通过
    screenshot
    来获得游戏内截图确认效果
  7. 修改脚本之后使用
    reload_script
    来重载,客户端不会自动重载
  8. 使用
    get_library
    可以查看某个library源码
  9. 确保所有功能跑通之后要求用户确认符合诉求
  10. 用户如果提出了进一步修改意见,则修改
  11. 如果用户确认符合诉求则问用户是否要清理调试代码等,清理掉调试代码等以便用户使用
  1. Understand the user's request: whether to create a new script or modify an existing one.
  2. Try to connect to MCP and use the
    ping
    tool to ensure the client is online. If the client is offline, inform the user that the client needs to be started to use MCP. Of course, you can still write scripts without MCP, but debugging will be very difficult.
  3. Read
    types.d.ts
    to understand the client API.
  4. Locate the file directory, find the script the user wants to modify or create a new script. You can use the MCP
    list_scripts
    tool for quick viewing.
  5. Write a test version of the script to ensure all logic works. During this process, you can debug using tools like
    eval
    ,
    get_errors
    ,
    grep_logs
    , and
    get_logs
    . Logs output via the
    log()
    interface can be retrieved via
    grep_logs
    and
    get_logs
    ; prioritize using
    grep_logs
    to save context.
  6. If the user is developing a visual feature, use
    screenshot
    to get an in-game screenshot to confirm the effect.
  7. After modifying the script, use
    reload_script
    to reload it. The client does not auto-reload.
  8. Use
    get_library
    to view the source code of a library.
  9. After ensuring all features work, ask the user to confirm that the requirements are met.
  10. If the user raises further modification suggestions, implement them.
  11. If the user confirms that the requirements are met, ask the user if they want to clean up debugging code, etc. Clean up debugging code and other unnecessary content for the user's convenience.

脚本的结构

Script Structure

一个脚本就是一个
.js
文件,加载时整体执行一次:先注册模块和设置, 再用
events.on(...)
挂行为:
js
client.registerModule("Auto Sprint", -1, "Movement", false);
client.registerSlider("Auto Sprint", "Delay", 5, 1, 20, 1);

events.on("tick", function () {
    if (!client.isEnabled("Auto Sprint")) return;
    // ...
});
脚本被自动预处理包在一个 IIFE 里执行(
(function(){ ...你的代码... })()
),所以顶层的
var
和
function
都是私有的,不同脚本之间不会互相覆盖。要跨脚本共享代码请用库
分类请用这六个之一:
Combat
Movement
Render
World
Player
Inventory
。
一个脚本可以注册多个模块、命令、HUD。
A script is a single
.js
file that executes entirely once when loaded: first register modules and settings, then attach behaviors with
events.on(...)
:
js
client.registerModule("Auto Sprint", -1, "Movement", false);
client.registerSlider("Auto Sprint", "Delay", 5, 1, 20, 1);

events.on("tick", function () {
    if (!client.isEnabled("Auto Sprint")) return;
    // ...
});
Scripts are automatically preprocessed and wrapped in an IIFE for execution (
(function(){ ...your code... })()
), so top-level
var
and
function
declarations are private and will not overwrite each other across different scripts. To share code across scripts, use libraries.
Use one of these six categories:
Combat
,
Movement
,
Render
,
World
,
Player
,
Inventory
.
A single script can register multiple modules, commands, and HUD elements.

Java调用

Java Interop

引擎提供 Nashorn 风格的 Java 互操作。
mc
以及从它拿到的一切(玩家、实体、物品、世界) 都是 Java 对象,直接调它们的方法就行:
js
mc.player.getX();
mc.player.getMainHandItem().getCount();
mc.level.getBlockState(pos).getBlock();
The engine provides Nashorn-style Java interoperation.
mc
and everything obtained from it (players, entities, items, worlds) are Java objects — you can directly call their methods:
js
mc.player.getX();
mc.player.getMainHandItem().getCount();
mc.level.getBlockState(pos).getBlock();

取类

Getting Classes

Java.type()
拿到一个类,可以用来
new
、读静态字段、调静态方法:
js
var BlockPos = Java.type("net.minecraft.core.BlockPos");
var Items = Java.type("net.minecraft.world.item.Items");
var Component = Java.type("net.minecraft.network.chat.Component");

var pos = new BlockPos(100, 64, 200);          // 构造
var stick = Items.STICK;                        // 静态字段
var text = Component.literal("hello");          // 静态方法
用点号写全限定名(
net.minecraft.core.BlockPos
),不是斜杠。 数组类型写
"int[]"
、
"java.lang.String[]"
。
内部类(嵌套类、枚举)用
$
分隔,不是点。
引擎直接把名字交给
Class.forName
,不会自动把点换成
$
,写错会报
Java class not found
:
js
Java.type("com.mojang.blaze3d.vertex.VertexFormat$Mode");        // 对
Java.type("net.minecraft.world.entity.Entity$RemovalReason");     // 对
Java.type("com.mojang.blaze3d.vertex.VertexFormat.Mode");         // 错,找不到类
枚举常量当静态字段取:
js
var Mode = Java.type("com.mojang.blaze3d.vertex.VertexFormat$Mode");
var quads = Mode.QUADS;
Java.type
的结果会缓存,重复调用不会有额外开销,可以放心写在事件回调里, 不过放到脚本顶层更清晰。
Use
Java.type()
to get a class, which can be used for
new
instances, reading static fields, and calling static methods:
js
var BlockPos = Java.type("net.minecraft.core.BlockPos");
var Items = Java.type("net.minecraft.world.item.Items");
var Component = Java.type("net.minecraft.network.chat.Component");

var pos = new BlockPos(100, 64, 200);          // Constructor
var stick = Items.STICK;                        // Static field
var text = Component.literal("hello");          // Static method
Write fully qualified names with dots (
net.minecraft.core.BlockPos
), not slashes.
For array types, write
"int[]"
,
"java.lang.String[]"
.
Inner classes (nested classes, enums) are separated by
$
, not dots.
The engine directly passes the name to
Class.forName
and does not automatically convert dots to
$
. Incorrect names will throw
Java class not found
:
js
Java.type("com.mojang.blaze3d.vertex.VertexFormat$Mode");        // Correct
Java.type("net.minecraft.world.entity.Entity$RemovalReason");     // Correct
Java.type("com.mojang.blaze3d.vertex.VertexFormat.Mode");         // Wrong, class not found
Enum constants are accessed as static fields:
js
var Mode = Java.type("com.mojang.blaze3d.vertex.VertexFormat$Mode");
var quads = Mode.QUADS;
The result of
Java.type
is cached, so repeated calls have no extra overhead. You can safely write it in event callbacks, though placing it at the top level of the script is clearer.

名字映射

Name Mapping

引擎在查字段和方法时会自己做重映射,所以使用 Mojang 名(
getX
、
getMainHandItem
)
如果你确定你写了正确的Mojang名仍然有问题,可以先使用
eval
进行探测,因为映射器不是100%能映射所有字段和方法。
类名同理,写
net.minecraft.core.BlockPos
即可。
The engine performs remapping automatically when looking up fields and methods, so use Mojang names (
getX
,
getMainHandItem
).
If you are sure you have written the correct Mojang name but still have issues, you can use
eval
to probe first, as the mapper does not map 100% of all fields and methods.
The same applies to class names — just write
net.minecraft.core.BlockPos
.

重载和类型转换

Overloading and Type Conversion

参数会按 Java 方法签名自动转换:JS 数字转
int
/
float
/
double
,字符串转
String
, JS 函数可以传给函数式接口(单方法接口)。有重载时引擎按参数个数和类型挑一个匹配的。
拿不准某个方法存不存在、返回什么,用
eval
当场试:
js
mc.player.getMainHandItem().getItem().toString()
Parameters are automatically converted according to Java method signatures: JS numbers convert to
int
/
float
/
double
, strings convert to
String
, and JS functions can be passed to functional interfaces (single-method interfaces). When there are overloads, the engine selects a matching one based on the number and type of parameters.
If you are unsure whether a method exists or what it returns, test it on the spot with
eval
:
js
mc.player.getMainHandItem().getItem().toString()

数组

Arrays

Java 数组比集合友好,有
.length
,也能用下标:
js
var arr = someJavaMethodReturningArray();
arr.length;
arr[0];
但集合不是数组,见下面的提示一节。
Java arrays are more user-friendly than collections: they have
.length
and can be accessed by index:
js
var arr = someJavaMethodReturningArray();
arr.length;
arr[0];
But collections are not arrays — see the Tips section below.

类型判断

Type Checking

js
var Player = Java.type("net.minecraft.world.entity.player.Player");
if (Java.isType(entity, Player)) { ... }
instanceof
不按 Java 类型工作(它走 JS 原型链),判断 Java 类型一律用
Java.isType
。
Java.isJavaObject(x)
判断一个值是不是 Java 对象。
js
var Player = Java.type("net.minecraft.world.entity.player.Player");
if (Java.isType(entity, Player)) { ... }
instanceof
does not work with Java types (it follows the JS prototype chain). Always use
Java.isType
to check Java types.
Java.isJavaObject(x)
checks whether a value is a Java object.

提示

Tips

一些脚本系统的规范等
Some specifications and notes for the script system.

Java 集合不是 JS 数组

Java Collections Are Not JS Arrays

mc.level.players()
返回的是
java.util.List
,没有
.length
。写
for (var i = 0; i < list.length; i++)
时条件是
0 < undefined
,循环一次都不执行, 而且不报错——这是最难发现的一类 bug。
js
var players = mc.level.players();
players.length                   // undefined
players.size(); players.get(0)   // 可以
Java.from(players).length        // 可以,转成真正的 JS 数组
Java.from()
接受 Java 数组和任何
Iterable
(List/Set/Collection)。要遍历
Map
得先
.entrySet()
或
.values()
。
mc.level.players()
returns a
java.util.List
, which does not have
.length
. When you write
for (var i = 0; i < list.length; i++)
, the condition becomes
0 < undefined
, so the loop never executes, and no error is thrown — this is one of the hardest types of bugs to find.
js
var players = mc.level.players();
players.length                   // undefined
players.size(); players.get(0)   // Works
Java.from(players).length        // Works, converts to a real JS array
Java.from()
accepts Java arrays and any
Iterable
(List/Set/Collection). To iterate over a
Map
, you first need to call
.entrySet()
or
.values()
.

设置项 id 永远是
"模块名:设置名"

Setting IDs Are Always
"ModuleName:SettingName"

注册时是两个参数,读取时是一个拼接的字符串:
js
client.registerSlider("My Module", "Speed", 1, 0, 5, 0.1);
client.getNumber("My Module:Speed");
HUD 的设置项不属于模块本身,必须用
client.hudProperty(hud, label)
生成 id,不要手拼。
Registration takes two parameters, but reading uses a concatenated string:
js
client.registerSlider("My Module", "Speed", 1, 0, 5, 0.1);
client.getNumber("My Module:Speed");
HUD settings do not belong to the module itself. You must use
client.hudProperty(hud, label)
to generate the ID — do not concatenate it manually.

HUD 必须回写
ctx.width
/
ctx.height

HUD Must Write Back
ctx.width
/
ctx.height

不写的话元素没有碰撞箱,用户在 HUD 编辑模式里拖不动也点不着,但画面看起来是正常的。
js
client.registerHud("Coords", { anchor: "Left Bottom", x: 4, y: 4 }, function (graphics, ctx) {
    var text = "X " + Math.round(mc.player.getX());
    render.drawString(graphics, text, ctx.x, ctx.y, 0xFFFFFFFF);
    ctx.width = render.getStringWidth(text);   // 必须
    ctx.height = render.fontHeight();          // 必须
});
Without these, the element has no hitbox, and users cannot drag or click it in HUD edit mode, even though it looks normal on screen.
js
client.registerHud("Coords", { anchor: "Left Bottom", x: 4, y: 4 }, function (graphics, ctx) {
    var text = "X " + Math.round(mc.player.getX());
    render.drawString(graphics, text, ctx.x, ctx.y, 0xFFFFFFFF);
    ctx.width = render.getStringWidth(text);   // Required
    ctx.height = render.fontHeight();          // Required
});

3D 标签要传实体,不要传坐标

3D Tags Should Take an Entity, Not Coordinates

传实体时引擎会用
partialTicks
做插值,实体移动时标签跟得住;自己传
getX()
拿到的是 tick 末的位置,标签会抖动、和实体渲染位置对不上。
js
render.drawTag("§e" + p.getName().getString(), p, 0.4, { autoScale: true });   // 推荐
render.drawTag("§aSpawn", 100.5, 65, 200.5, {});                               // 固定位置才这么写
第三个参数是在实体身高之上的额外偏移,碰撞箱高度引擎已经算进去了。
When passing an entity, the engine uses
partialTicks
for interpolation, so the tag follows the entity smoothly when it moves. If you pass coordinates obtained from
getX()
yourself, you get the position at the end of the tick, causing the tag to jitter and not align with the entity's rendered position.
js
render.drawTag("§e" + p.getName().getString(), p, 0.4, { autoScale: true });   // Recommended
render.drawTag("§aSpawn", 100.5, 65, 200.5, {});                               // Only use for fixed positions
The third parameter is the extra offset above the entity's height; the engine already accounts for the hitbox height.

颜色是 ARGB,alpha 在最前

Colors Are ARGB, Alpha First

0xFF55FF55
是不透明的绿色。漏掉 alpha 写成
0x55FF55
,alpha 就是 0,画出来完全透明—— 看起来像"没画出来"。
文本颜色代码用
§
(U+00A7),例如
"§c红色"
、
"§l粗体"
。
0xFF55FF55
is opaque green. If you omit the alpha channel and write
0x55FF55
, the alpha value is 0, making it completely transparent — it looks like "nothing was drawn".
Text color codes use
§
(U+00A7), e.g.,
"§cRed"
,
"§lBold"
.

模块开关要自己判断

Module Enable/Disable Must Be Checked Manually

注册模块不会让事件只在开启时触发。每个回调都要自己检查:
js
events.on("tick", function () {
    if (!client.isEnabled("My Module")) return;
    // ...
});
也可以监听
enable
/
disable
事件做初始化和清理。
当然,你可以写其他模块的附属功能,开启某个其他已有模块才执行的逻辑。
Registering a module does not make events only fire when it is enabled. Every callback must check this on its own:
js
events.on("tick", function () {
    if (!client.isEnabled("My Module")) return;
    // ...
});
You can also listen to
enable
/
disable
events for initialization and cleanup.
Of course, you can write auxiliary features for other modules — logic that only executes when another existing module is enabled.

事件

Events

三类,写法不同:
通知型——只是告诉你发生了什么:
js
events.on("tick", function () { ... });
events.on("render3d", function (partialTicks) { ... });
可取消型——
return true
阻止默认行为:
js
events.on("chat", function (text) {
    return text === "secret";     // true = 不发送
});
可修改型——收到一个对象,改它的字段:
js
events.on("movementInput", function (e) { e.jump = true; });
events.on("fov", function (e) { e.factor = 1.5; });
可修改型事件里
return
是没用的,必须写字段。哪个事件属于哪类、字段叫什么,
types.d.ts
里每条
on(...)
重载都有注明。
渲染分两个事件:
render2d
画屏幕 UI(给你
graphics
),
render3d
画世界内容 (
render.drawTag
/
render.drawBox
只能在这里调)。
There are three types, with different usage patterns:
Notification type — only informs you that something happened:
js
events.on("tick", function () { ... });
events.on("render3d", function (partialTicks) { ... });
Cancelable type —
return true
to prevent the default behavior:
js
events.on("chat", function (text) {
    return text === "secret";     // true = do not send
});
Modifiable type — receives an object, modify its fields:
js
events.on("movementInput", function (e) { e.jump = true; });
events.on("fov", function (e) { e.factor = 1.5; });
In modifiable events,
return
has no effect — you must modify the fields. Which category each event belongs to and what its fields are called are noted in each
on(...)
overload in
types.d.ts
.
There are two rendering events:
render2d
for drawing screen UI (provides
graphics
), and
render3d
for drawing world content (
render.drawTag
/
render.drawBox
can only be called here).

3.2.0 更新内容

3.2.0 Update Notes

客户端 3.2.0 对脚本系统进行了大更新(强制更新,不需要判断版本,以下特性均可直接使用),如果你保留了之前的记忆需要了解如下更新的新特性:
Client version 3.2.0 brings major updates to the script system (it is a mandatory update, no version check is needed — all the following features can be used directly). If you have memories of previous versions, you need to understand the new features below:

Java特性

Java Features

特性示例
Method Reference
list.stream().map(java.lang.String::length)
、
java.lang.Integer::parseInt
、
'text'::length
、
X::new
包名直接访问类
var File = java.io.File;
net.minecraft.core.BlockPos
Packages
根
new Packages.java.util.HashMap()
JavaImporter
+
with
with (new JavaImporter(java.io, java.util)) { new File('a'); new HashMap(); }
函数自动转接口(SAM)
new java.lang.Thread(function () { ... }).start()
匿名类
new java.lang.Runnable() { run: function () { ... } }
继承类 / 实现多个接口
Java.extend(java.util.ArrayList, { add: function (x) { return Java.super(this).add(x); } })
Java.super
在覆盖方法里调用父类实现
Java.to
Java.to([1, 2], 'int[]')
X.class
java.util.ArrayList.class
(Nashorn 兼容)
迭代 Java 集合
for (var p of mc.level.players())
、
[...list]
、
var [a, b] = list
;Map 产出
[key, value]
,也支持 Iterator、Stream、Java 数组
对象相等
mc.player === mc.player
为 true;
===
、
Object.is
、Map/Set 的键都按底层 Java 对象比较
instanceof
关键字
p instanceof net.minecraft.world.entity.player.Player
;
Java.extend
生成的类也支持
JS 数组传给 Java自动转成 Java 数组或
List
,varargs 方法可以直接传数组:
LongStream.of([1, 2, 3])
静态字段实时读写,并按字段类型转换(以前读到的是快照);final 字段只读
FeatureExample
Method Reference
list.stream().map(java.lang.String::length)
,
java.lang.Integer::parseInt
,
'text'::length
,
X::new
Direct class access via package name
var File = java.io.File;
net.minecraft.core.BlockPos
Packages
root
new Packages.java.util.HashMap()
JavaImporter
+
with
with (new JavaImporter(java.io, java.util)) { new File('a'); new HashMap(); }
Automatic function-to-interface conversion (SAM)
new java.lang.Thread(function () { ... }).start()
Anonymous classes
new java.lang.Runnable() { run: function () { ... } }
Class inheritance / implementing multiple interfaces
Java.extend(java.util.ArrayList, { add: function (x) { return Java.super(this).add(x); } })
Java.super
Call the parent class implementation in an overridden method
Java.to
Java.to([1, 2], 'int[]')
X.class
java.util.ArrayList.class
(Nashorn compatible)
Iterating Java collections
for (var p of mc.level.players())
,
[...list]
,
var [a, b] = list
; Maps yield
[key, value]
, also supports Iterator, Stream, Java arrays
Object equality
mc.player === mc.player
is true;
===
,
Object.is
, and Map/Set keys all compare based on the underlying Java object
instanceof
keyword
p instanceof net.minecraft.world.entity.player.Player
; also supported for classes generated by
Java.extend
Passing JS arrays to JavaAutomatically converted to Java arrays or
List
; varargs methods can directly accept arrays:
LongStream.of([1, 2, 3])
Static fieldsRead and write in real time, with type conversion based on field type (previously, reads returned snapshots); final fields are read-only

客户端API

Client API

旧的
client.getBool(...)
等扁平接口全部保留,新增一套对象接口:
js
var aura = moduleManager.getModule('Legit Aura');
aura.enabled = true;                         // 与点击 GUI 一致(有通知)
aura.key = 'R';                              // 支持键名
for (var p of aura.getProperties()) {
    log(p.label, p.type, p.value, p.visible);
}
var mode = aura.getProperty('Mode');
mode.cycle();                                // Single -> Switch
mode.children;                               // 只返回当前条件下可见的子设置
  • moduleManager
    :
    getModule
    、
    getModules(category?)
    、
    getEnabledModules
    、
    getCategories
    、
    getCategoryParents
    、
    getProperty(id)
    、
    hasModule
    。
  • Module
    • 基本信息:
      name
      、
      category
      /
      categoryParent
      /
      categoryLeaf
      、
      description
      (可写)、
      icon
      、
      inline
      、
      script
      、
      keyName
      。
    • 可读写:
      enabled
      、
      key
      。
    • 方法:
      toggle
      /
      enable
      /
      disable
      /
      setEnabled(v, silent)
      、
      getProperties
      /
      getAllProperties
      /
      getProperty
      、
      getBinds
      、
      bind(key, options)
      。
  • Property
    • 可读写:
      value
      ,写入时会校验:滑块限制范围并对齐步长,mode 拒绝未知选项,multi 丢弃未知选项。
    • 只读信息:
      type
      、
      defaultValue
      、
      min
      /
      max
      /
      inc
      、
      options
      、
      optionIcons
      、
      hint
      、
      condition
      。
    • 界面相关:
      visible
      (包含父级条件)、
      hidden
      (可写)、
      label
      (可写)。
    • 层级关系:
      parent
      、
      children
      、
      swatch
      。
    • 方法:
      reset
      、
      toggle
      、
      cycle
      、
      select
      /
      deselect
      /
      isSelected
      /
      toggleOption
      。
  • 包装对象会被缓存(同一模块/属性总是同一个对象),每次读取都拿实时状态;对象被注销后再访问会抛
    ReferenceError
    。
js
var bind = keybindManager.add(aura, 'KP_8', { mode: 'hold' });
bind.key = 'MOUSE4';
bind.remove();

var m = macroManager.add('Message', { name: 'gg', message: '/hub', key: 'H' });
m.run();
  • keybindManager
    :
    getBinds
    、
    getBind
    、
    find(target)
    、
    add
    、
    remove
    、
    keyCode
    、
    keyName
    。
  • Bind
    :
    target
    、
    key
    、
    mode
    、
    value
    、
    visible
    、
    isModule
    、
    module
    、
    property
    、
    update()
    、
    remove()
    。
  • macroManager
    :
    getMacros
    、
    getMacro
    、
    add('Message' | 'Item', options)
    、
    remove
    、
    run
    。
  • Macro
    :
    name
    、
    key
    、
    message
    、
    item
    、
    match
    、
    rotation
    、
    switchMode
    、
    yawOffset
    、
    pitch
    、
    update()
    、
    run()
    、
    remove()
    。
事件参数说明
click
(button, action, x, y)
所有鼠标按键的按下/松开,返回 true 取消
scroll
(delta, x, y)
滚轮,返回 true 取消(例如阻止切换快捷栏)
charTyped
(char, codepoint)
字符输入(支持中文),返回 true 取消
keyInput
(key, action)
所有键盘事件(按下 1 / 松开 0 / 重复 2),任何界面下都触发,返回 true 可吞掉按键
原有的
key
事件语义不变(只在游戏中或容器界面里触发,只报告按下)。
input
提供:
getMouseX/Y
(和
render2d
同一套 GUI 坐标)、
getScreenWidth/Height
、
getGuiScale
、
isMouseDown
、
isKeyDown
、
isMouseGrabbed
/
setMouseGrabbed
(释放或抓取鼠标)、
isScreenOpen
、
keyCode
/
keyName
。
键名:凡是需要按键的地方都接受
'R'
、
'F5'
、
'LSHIFT'
、
'KP_9'
/
'NUMPAD9'
、
'M4'
/
'MOUSE4'
、
'key.keyboard.r'
,或直接传键码。
keyName
和
keyCode
可以互相转换回来。
  • render.bloom(graphics, x, y, w, h, radius, color, spread, intensity)
    :圆角矩形发光。
  • client.getUsername()
    、
    client.getVersion()
    、
    client.getMinecraftVersion()
    。
All old flat interfaces like
client.getBool(...)
are fully retained, and a new set of object-oriented interfaces has been added:
js
var aura = moduleManager.getModule('Legit Aura');
aura.enabled = true;                         // Consistent with clicking the GUI (triggers notification)
aura.key = 'R';                              // Supports key names
for (var p of aura.getProperties()) {
    log(p.label, p.type, p.value, p.visible);
}
var mode = aura.getProperty('Mode');
mode.cycle();                                // Single -> Switch
mode.children;                               // Only returns child settings visible under current conditions
  • moduleManager
    :
    getModule
    ,
    getModules(category?)
    ,
    getEnabledModules
    ,
    getCategories
    ,
    getCategoryParents
    ,
    getProperty(id)
    ,
    hasModule
    .
  • Module
    • Basic info:
      name
      ,
      category
      /
      categoryParent
      /
      categoryLeaf
      ,
      description
      (writable),
      icon
      ,
      inline
      ,
      script
      ,
      keyName
      .
    • Read/write:
      enabled
      ,
      key
      .
    • Methods:
      toggle
      /
      enable
      /
      disable
      /
      setEnabled(v, silent)
      ,
      getProperties
      /
      getAllProperties
      /
      getProperty
      ,
      getBinds
      ,
      bind(key, options)
      .
  • Property
    • Read/write:
      value
      , which is validated on write: sliders are limited to range and aligned to step size, mode rejects unknown options, multi discards unknown options.
    • Read-only info:
      type
      ,
      defaultValue
      ,
      min
      /
      max
      /
      inc
      ,
      options
      ,
      optionIcons
      ,
      hint
      ,
      condition
      .
    • UI-related:
      visible
      (includes parent conditions),
      hidden
      (writable),
      label
      (writable).
    • Hierarchy:
      parent
      ,
      children
      ,
      swatch
      .
    • Methods:
      reset
      ,
      toggle
      ,
      cycle
      ,
      select
      /
      deselect
      /
      isSelected
      /
      toggleOption
      .
  • Wrapper objects are cached (the same module/property always returns the same object), and each read gets the real-time state; accessing an object after it has been unregistered will throw a
    ReferenceError
    .
js
var bind = keybindManager.add(aura, 'KP_8', { mode: 'hold' });
bind.key = 'MOUSE4';
bind.remove();

var m = macroManager.add('Message', { name: 'gg', message: '/hub', key: 'H' });
m.run();
  • keybindManager
    :
    getBinds
    ,
    getBind
    ,
    find(target)
    ,
    add
    ,
    remove
    ,
    keyCode
    ,
    keyName
    .
  • Bind
    :
    target
    ,
    key
    ,
    mode
    ,
    value
    ,
    visible
    ,
    isModule
    ,
    module
    ,
    property
    ,
    update()
    ,
    remove()
    .
  • macroManager
    :
    getMacros
    ,
    getMacro
    ,
    add('Message' | 'Item', options)
    ,
    remove
    ,
    run
    .
  • Macro
    :
    name
    ,
    key
    ,
    message
    ,
    item
    ,
    match
    ,
    rotation
    ,
    switchMode
    ,
    yawOffset
    ,
    pitch
    ,
    update()
    ,
    run()
    ,
    remove()
    .
EventParametersDescription
click
(button, action, x, y)
All mouse button presses/releases, return true to cancel
scroll
(delta, x, y)
Mouse wheel, return true to cancel (e.g., prevent hotbar switching)
charTyped
(char, codepoint)
Character input (supports Chinese), return true to cancel
keyInput
(key, action)
All keyboard events (press 1 / release 0 / repeat 2), triggered in any interface, return true to consume the key
The semantics of the original
key
event remain unchanged (only triggered in-game or in container interfaces, only reports presses).
input
provides:
getMouseX/Y
(same GUI coordinate system as
render2d
),
getScreenWidth/Height
,
getGuiScale
,
isMouseDown
,
isKeyDown
,
isMouseGrabbed
/
setMouseGrabbed
(release or grab mouse),
isScreenOpen
,
keyCode
/
keyName
.
Key names: Anywhere a key is required, it accepts
'R'
,
'F5'
,
'LSHIFT'
,
'KP_9'
/
'NUMPAD9'
,
'M4'
/
'MOUSE4'
,
'key.keyboard.r'
, or directly passing a key code.
keyName
and
keyCode
can be converted to each other.
  • render.bloom(graphics, x, y, w, h, radius, color, spread, intensity)
    : Rounded rectangle glow effect.
  • client.getUsername()
    ,
    client.getVersion()
    ,
    client.getMinecraftVersion()
    .

Forge 事件 Hook

Forge Event Hook

js
var id = events.onForge('LivingDeathEvent', function (e) { log(e.getEntity()); });
events.onForge('ClientChatReceivedEvent', function (e) { return /spam/.test(e.getMessage().getString()); }); // true = 取消
events.offForge(id);
  • 事件类型可以传 Java 类,也可以传名字:简单名(
    LivingDeathEvent
    )、嵌套名(
    RenderGuiOverlayEvent.Pre
    )或全名,子类同样会匹配。
  • 选项:
    phase: 'pre' | 'post'
    (在 Forge 自己的监听器之前或之后)、
    receiveCanceled
    、
    anyThread
    (默认只收主线程发出的事件)。
js
var id = events.onForge('LivingDeathEvent', function (e) { log(e.getEntity()); });
events.onForge('ClientChatReceivedEvent', function (e) { return /spam/.test(e.getMessage().getString()); }); // true = cancel
events.offForge(id);
  • Event types can be passed as Java classes or by name: simple name (
    LivingDeathEvent
    ), nested name (
    RenderGuiOverlayEvent.Pre
    ), or full name. Subclasses will also be matched.
  • Options:
    phase: 'pre' | 'post'
    (before or after Forge's own listeners),
    receiveCanceled
    ,
    anyThread
    (default only receives events from the main thread).

Event Loop

Event Loop

setTimeout
、
setInterval
、
clearTimeout
、
clearInterval
、
queueMicrotask
,Promise/async。
setTimeout
,
setInterval
,
clearTimeout
,
clearInterval
,
queueMicrotask
, Promise/async.

Fetch(WHATWG)

Fetch (WHATWG)

js
const res = await fetch('https://example.com/api', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ a: 1 }),
});
const data = await res.json();
  • 重定向支持
    follow
    、
    manual
    、
    error
    三种模式,最多 20 跳。
  • 支持
    AbortController
    /
    AbortSignal
    ,包括
    AbortSignal.timeout
    /
    any
    。
  • 流式读取:响应头一到就 resolve,响应体通过
    res.body
    (
    ReadableStream
    )分块到达。
  • 自动发送
    accept-encoding: gzip, deflate
    并自动解压。
  • 配套对象:
    Request
    、
    Response
    (包括
    json
    /
    redirect
    /
    error
    /
    clone
    )、
    Headers
    、
    URL
    、
    URLSearchParams
    、
    Blob
    、
    TextEncoder
    、
    TextDecoder
    、
    DOMException
    。
js
const res = await fetch('https://example.com/api', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ a: 1 }),
});
const data = await res.json();
  • Redirects support three modes:
    follow
    ,
    manual
    ,
    error
    , with a maximum of 20 hops.
  • Supports
    AbortController
    /
    AbortSignal
    , including
    AbortSignal.timeout
    /
    any
    .
  • Streaming read: Resolves as soon as response headers arrive, and the response body arrives in chunks via
    res.body
    (
    ReadableStream
    ).
  • Automatically sends
    accept-encoding: gzip, deflate
    and automatically decompresses responses.
  • Supporting objects:
    Request
    ,
    Response
    (including
    json
    /
    redirect
    /
    error
    /
    clone
    ),
    Headers
    ,
    URL
    ,
    URLSearchParams
    ,
    Blob
    ,
    TextEncoder
    ,
    TextDecoder
    ,
    DOMException
    .

Deno

Deno

js
const server = Deno.serve({ port: 8080 }, async (req) => {
  const url = new URL(req.url);
  switch (url.pathname) {
    case '/hello': return new Response('hi');
    case '/echo':  return Response.json(await req.json());
    case '/sse':   return new Response(new ReadableStream({
      async pull(c) { await new Promise(r => setTimeout(r, 1000)); c.enqueue('data: tick\n\n'); },
      cancel() { log('client left'); }
    }), { headers: { 'content-type': 'text/event-stream' } });
    default: return new Response('not found', { status: 404 });
  }
});
js
const server = Deno.serve({ port: 8080 }, async (req) => {
  const url = new URL(req.url);
  switch (url.pathname) {
    case '/hello': return new Response('hi');
    case '/echo':  return Response.json(await req.json());
    case '/sse':   return new Response(new ReadableStream({
      async pull(c) { await new Promise(r => setTimeout(r, 1000)); c.enqueue('data: tick\n\n'); },
      cancel() { log('client left'); }
    }), { headers: { 'content-type': 'text/event-stream' } });
    default: return new Response('not found', { status: 404 });
  }
});