Tags
Creators
Details
6.2
Compatibility
Changes
✨ Features / 新特性
- Enum 增强 · Enum Enhancements 用户自定义枚举现在可与字符串直接比较(MyColors.RED == "RED" 双向成立),支持块级局部 enum 定义(进入 block 时快照、退出时恢复),补全了 org.bukkit.event.*、org.bukkit.inventory.meta、org.bukkit.block.data 等常用包的扫描列表,并为 Java 枚举常量与枚举类补齐固有方法调用:实例侧支持 name() / ordinal() / toString() / compareTo() / equals(),类侧支持 values() / valueOf(),且 valueOf 与 compareTo 均支持忽略大小写的字符串回退匹配。
User-defined enums can now be compared directly with strings (MyColors.RED == "RED" works bidirectionally). Block-scoped local enum definitions are supported via snapshot/restore on block entry/exit. The default package scan list has been expanded to cover org.bukkit.event.*, org.bukkit.inventory.meta, org.bukkit.block.data, and other commonly used packages. Native methods on Java enum constants and enum classes are now callable: instance-side name() / ordinal() / toString() / compareTo() / equals(), class-side values() / valueOf(), with case-insensitive string fallback in valueOf and compareTo.
- 类解析统一入口 · Unified Class Resolution 新增 EnumRegistry.resolveClass,作为全项目唯一的类加载入口:传入含 . 的完整路径时直接 Class.forName 并缓存;传入简单名时按预设包列表扫描,命中后写入 simpleToFqcn 与 classCache 两级缓存,避免重复扫描。resolveEnumClass 在其之上做 isEnum() 过滤,语法层面无需区分枚举与其他类。
Introduced EnumRegistry.resolveClass as the single class-loading entry point for the entire project: fully-qualified paths (containing .) go straight to Class.forName and are cached; simple names are scanned against the preset package list and, once resolved, written into the two-level simpleToFqcn / classCache to avoid repeated scanning. resolveEnumClass is layered on top with an isEnum() filter, so the grammar layer no longer needs to distinguish enums from ordinary classes.
- new 关键字 · The new Keyword 新增 new 关键字,语法形如 new ItemStack(Material.DIAMOND_BLOCK, 14),类名支持简单名(自动扫描)与完整路径(直接加载)两种形式。构造器匹配支持重载筛选、原生类型自动装箱/拆箱、EntityRef → Entity 解包、String → Enum 隐式转换。构造出的对象可直接赋值给 var 变量,并立即获得该类及其父类、接口定义的全部有效方法(经由反射调用)。
Added the new keyword with syntax such as new ItemStack(Material.DIAMOND_BLOCK, 14). Class names accept both simple names (auto-scanned) and fully-qualified paths (loaded directly). Constructor matching supports overload resolution, primitive autoboxing/unboxing, EntityRef → Entity unwrapping, and implicit String → Enum conversion. The resulting object can be assigned to a var variable and immediately exposes every method inherited from its class hierarchy and interfaces via reflection.
Example
int main(){ Player p = @p; var aa = new org.bukkit.inventory.ItemStack(Material.DIAMOND_BLOCK,40); var bb = new org.bukkit.inventory.ItemStack(Material.NETHERITE_BLOCK,40); var cc = new org.bukkit.inventory.ItemStack(Material.GOLD_BLOCK,40); p .sendMessage("HI!"); p .getInventory() .addItem(aa,bb,cc); var aworld=Bukkit.getWorld("world"); float yaw=45.0;float pit=45.0; var loc = new Location (aworld,64.0,64.0,64.0,toFloat(yaw),toFloat(pit)); } 4. 静态方法调用 · Static Method Invocation 支持直接调用 Java 类的静态方法,例如 Bukkit.getWorld("world") 或 org.bukkit.Bukkit.getWorld("world")。visitPostfix 现在会做一次「类前缀预扫描」:从 primary 起收集连续的 .IDENTIFIER 段,遇到紧跟 ( 的段即停止,先尝试最长路径的枚举解析,再尝试最长路径的类解析。凡是反射返回原生 Entity / Player,一律自动包装为 EntityRef,从而立刻具备字段实时读取与反射方法调用的能力。
Direct invocation of static methods on Java classes is now supported, e.g. Bukkit.getWorld("world") or org.bukkit.Bukkit.getWorld("world"). visitPostfix performs a "class-prefix pre-scan": starting from primary, it collects consecutive .IDENTIFIER segments, stopping at any segment immediately followed by (. It first tries the longest matching enum path, then the longest matching class path. Whenever a reflection call returns a native Entity / Player, it is automatically wrapped into an EntityRef, immediately gaining live field reads and reflective method invocation.
- Varargs 反射统一修复 · Unified Varargs Reflection Fix 反射层全面支持 Java 可变参数方法。canConvertArgs / convertArgs 统一接收 isVarArgs 参数,并区分两种情形:剩余单参本身即为数组 / List 时直接传递;剩余 N 个参数时逐个转换为元素类型并打包成 T[N]。invokeMethod、invokeStaticMethod、visitNewExpr、invokeEnumStaticMethod 以及 EntityRef.findMethod 全部走同一套逻辑,一劳永逸地修掉了 PlayerInventory.addItem(ItemStack...)、Bukkit.broadcastMessage(String...) 等一批方法「找不到匹配」的老问题。
The reflection layer now fully supports Java varargs methods. canConvertArgs / convertArgs uniformly accept an isVarArgs flag and distinguish two cases: a single trailing argument that is already an array / List is passed through directly; N trailing arguments are converted element-by-element and packed into a T[N]. invokeMethod, invokeStaticMethod, visitNewExpr, invokeEnumStaticMethod, and EntityRef.findMethod all share this logic, permanently fixing "no matching method" errors on PlayerInventory.addItem(ItemStack...), Bukkit.broadcastMessage(String...), and similar APIs.
- toFloat 内置函数 · toFloat Builtin 新增 toFloat(x) 内置函数,与既有的 toInt / toDouble / toLong / toBool 保持一致的调用形式,用于在脚本层显式将数值或字符串转换为 float,帮助在 Float 与 Double 重载歧义场景下精确定位目标方法。
Added the toFloat(x) builtin, consistent in style with the existing toInt / toDouble / toLong / toBool. It explicitly converts a number or string to float at the script level, useful for pinning down the intended target when a Float / Double overload ambiguity arises.
- 函数重载 · Function Overloading 脚本函数支持重载。runtime.functions 由 Map<String, FunctionDef> 升级为 Map<String, List<FunctionDef>>,注册阶段以「参数类型签名」(参数名不参与、未声明类型记为 *)去重,同名同签名冲突时明确报错。调用阶段先按参数个数过滤,再按 int / double / bool / string 粗类型匹配,最后以「具体类型数量多者优先」打破平局,仍歧义则报错。@BukkitRunnable / @EventHandler 等注解函数通过 resolveUniqueFunction 强制要求唯一实现,避免事件路由歧义。
Script functions now support overloading. runtime.functions was upgraded from Map<String, FunctionDef> to Map<String, List<FunctionDef>>. Registration deduplicates by "parameter type signature" (parameter names excluded, undeclared types recorded as *) and explicitly rejects same-name same-signature collisions. Resolution first filters by arity, then by coarse type (int / double / bool / string), then breaks ties by preferring the implementation with more concrete types, and reports ambiguity otherwise. Annotated functions such as @BukkitRunnable / @EventHandler are required to be unique via resolveUniqueFunction, avoiding event-routing ambiguity.
Example
int foo(int a) { return a + 1; } int foo(int a, int b) { return a + b; } string foo(string ss) { return ss + "!"; }
int main() { var x = foo(1); // 1 参 int → 2 var y = foo(1, 2); // 2 参 int → 3 var z = foo("hi"); // 1 参 string → "hi!"
} 8. Lambda 表达式 · Lambda Expressions 新增 lambda 语法,向 Java 对齐:无参与多参使用 (),单参数可省略括号,形如 x -> x + 1、(a, b) -> a + b、() -> foo()。体可为块({ ... },需显式 return)或表达式(隐式返回)。参数支持可选类型标注 (int a, int b) -> ...,声明类型时由运行期做粗类型检查与转换,未声明时由用户自行负责。闭包捕获按引用语义实现,外部变量在 lambda 内部的后续修改可见。
Added lambda syntax aligned with Java: () for zero or multi-parameter forms, parentheses omitted for a single parameter, e.g. x -> x + 1, (a, b) -> a + b, () -> foo(). Bodies may be blocks ({ ... }, requiring explicit return) or expressions (implicit return). Parameters accept optional type annotations such as (int a, int b) -> ...; when declared, the runtime performs coarse type checking and conversion, otherwise the user takes responsibility. Closure capture is by reference, so subsequent mutations to outer variables are visible inside the lambda.
- Lambda 即函数式接口 · Lambda as Functional Interface Lambda 可通过 new Foo(lambda) 直接实例化为函数式接口,也可作为普通实参传给任意需要函数式接口的 Java 方法,例如 Bukkit.getScheduler().runTaskLater(plugin, () -> ..., 20L)。适配层基于 java.lang.reflect.Proxy,LambdaValue.adaptTo(iface) 与静态 findSAM 集中承载 SAM 识别与代理生成。返回值按 SAM 声明的类型自动转换(void / 数值 / 布尔 / String / 枚举均已覆盖)。参数匹配阶段新增「lambda 形参个数 == SAM 形参个数」校验,自动消解 Runnable 与 Consumer 之间的重载歧义。泛型在 JVM 侧已擦除,无需额外处理。
Lambdas can be instantiated directly as functional interfaces via new Foo(lambda), or passed as ordinary arguments to any Java method requiring one, e.g. Bukkit.getScheduler().runTaskLater(plugin, () -> ..., 20L). The adapter is backed by java.lang.reflect.Proxy, with LambdaValue.adaptTo(iface) and the static findSAM centralizing SAM detection and proxy generation. Return values are automatically converted per the SAM's declared type (void / numeric / boolean / String / enum all covered). Argument matching now enforces "lambda arity == SAM arity", automatically resolving overload ambiguity between Runnable and Consumer. Generics are erased on the JVM side and require no extra handling.
Examples:
var f1 = () -> 42; var f2 = x -> x * 2; var f3 = (a, b) -> a + b; var f4 = (a, b) -> { return a * b; }; var outt = 123; var f5 = (int a, int b) -> a - b; var f6 = () -> outt;
int main() { var a = f2(5); // 10 var b = f3(2, 3); // 5 var c = f4(4, 5); // 20 var d = f5(10, 3); // 7 var e = f6(); } int main() { Player p = @p; var a = Bukkit.getPluginManager() .getPlugin ("HimDSL"); long b = 20; Bukkit.getScheduler().runTaskLater ( a, ()->Bukkit.getServer().getLogger().info("任务执行了!"), toLong(b) ); } int main() { var list = new java.util.ArrayList(); list.add("hello"); list.add("world"); // ArrayList.forEach 需要 Consumer,lambda 应被适配 list.forEach(x -> Bukkit.getLogger().info("item: " + x)); } int main() { var f = new java.util.function.Function(x -> x); } 10. 自增自减语法 · Increment / Decrement Operators 新增 i++ / i-- / ++i / --i 四种形式。前缀形式作用于任意 lvalue,返回新值;后缀形式作用于 postfix 末尾,返回旧值。for 循环的更新段现在接受任意表达式(forUpdate: assignNoSemi | expression),从而支持 for (var i = 0; i < 3; i++) 与 for (var i = 3; i > 0; i--) 这类惯用写法。词法层通过最长匹配将 i++ 与 i + +j 正确区分。
Added i++ / i-- / ++i / --i. Prefix forms operate on any lvalue and return the new value; postfix forms attach to the end of a postfix and return the old value. The for loop update clause now accepts arbitrary expressions (forUpdate: assignNoSemi | expression), enabling idiomatic forms like for (var i = 0; i < 3; i++) and for (var i = 3; i > 0; i--). The lexer distinguishes i++ from i + +j via longest-match rules.
Example
int main() { var i = 0; i++; // i=1,返回 0 丢弃 ++i; // i=2 var a = i++; // a=2, i=3 var b = ++i; // b=4, i=4 var c = i--; // c=4, i=3 var d = --i; // d=2, i=2
for (var j = 0; j < 3; j++) {
Bukkit.getLogger().info("j=" + j);
}
// 数组 / 字段 / 字符串
var arr = {10, 20, 30};
arr[0]++; // arr[0] = 11
var ss = "abc";
ss[0]++; // "bbc"
Bukkit.getLogger().info(i+" "+a+" "+b+" "+c+" "+d+" "+j+" "+arr[0]+" "+ss);
} 11. GUI / GUISlot 结构体 · GUI / GUISlot Structs 新增 GUI 与 GUISlot 两个反射式结构体。new GUI(rows, title) 创建 1~6 行的容器,内部以 rows × 9 的二维网格存储槽位,GuiRef 继承 AbstractList<List<GuiSlotRef>> 从而天然支持 gui[r][c] 与 gui[r][c] = slot 索引语法。new GUISlot() 默认图标为 Material.AIR、无回调,可选的 new GUISlot(itemStack) / new GUISlot(material) 直接指定图标。GUISlot 提供 icon / name / lore 字段,以及 onLeftClick / onRightClick / onShiftLeftClick / onShiftRightClick 四个回调,只接受 LambdaValue 或 Runnable。GUI 提供 open(player) / close(player) / update(row, col) / updateAll() / onClose(cb) 五个方法,update 与 updateAll 会主动刷新所有正在打开该 GUI 的玩家视图。
Introduced two reflective structs, GUI and GUISlot. new GUI(rows, title) creates a 1–6 row container backed by an internal rows × 9 grid; GuiRef extends AbstractList<List<GuiSlotRef>>, giving natural support for gui[r][c] and gui[r][c] = slot index syntax. new GUISlot() defaults to Material.AIR with no callbacks; new GUISlot(itemStack) / new GUISlot(material) supply an icon directly. GUISlot exposes icon / name / lore fields plus four callbacks — onLeftClick / onRightClick / onShiftLeftClick / onShiftRightClick — accepting only LambdaValue or Runnable. GUI exposes open(player) / close(player) / update(row, col) / updateAll() / onClose(cb); update and updateAll proactively refresh every viewer currently viewing the GUI.
Example
int main() { var g = new GUI(3, "§6主菜单");
var diamond = new GUISlot();
diamond.icon = new org.bukkit.inventory.ItemStack(Material.DIAMOND, 1);
diamond.name = "§b点我";
diamond.lore = {"§7左键换金锭", "§7右键关闭"};
diamond.onLeftClick = () -> {
Player p = @s;
p.sendMessage("§a你点了钻石");
g[0][0].icon = new org.bukkit.inventory.ItemStack(Material.GOLD_INGOT, 1);
//diamond.icon = new org.bukkit.inventory.ItemStack(Material.GOLD_INGOT, 1);
g.update(0, 0); // 单个槽位更新
};
diamond.onRightClick = () -> {
Player p = @s;
g.close(p);
};
g[0][0] = diamond;
g[1][1] = new GUISlot(Material.EMERALD);
g[2][8] = new GUISlot(Material.BARRIER);
g.updateAll(); // 全局刷新
g.onClose(() -> {
Player p = @s;
p.sendMessage("§7GUI 关闭");
});
g.open(@p);
} 12. 通用引用接口 RefLike · Generic RefLike Interface 抽象出 RefLike 接口(hasField / getField / setField / invoke),EntityRef、GuiRef、GuiSlotRef 共同实现。visitPostfix 的字段访问与方法调用、doAssignment 的字段写回全部统一走 RefLike,未来接入新的反射式对象只需实现该接口,无需再改核心调度逻辑。EntityRef 的 setField 一律抛出「字段只读」,与原有语义保持一致。
Abstracted the RefLike interface (hasField / getField / setField / invoke), jointly implemented by EntityRef, GuiRef, and GuiSlotRef. Field access and method dispatch in visitPostfix, as well as field writes in doAssignment, all route through RefLike. Adding a new reflective object in the future only requires implementing this interface — the core dispatch logic stays untouched. EntityRef.setField throws "field is read-only" uniformly, preserving existing semantics.
- Lvalue 保序路径重构 · Order-Preserving Lvalue Path LvalueInfo 从"indices 与 fields 分离存储"重构为"保序 path 列表"(元素为 Integer 索引或 String 字段),完整保留源语法中的访问顺序。doAssignment 与 readLvalue 改为按序执行 readKey / writeKey,从而支持 g[0][0].icon = item 这类"先索引后字段"的深层写入,同时保持 s.name[0] = 'X' 这类"先字段后索引"的既有行为。字符串索引写入统一走"读—改—重写父容器"三步,复合赋值仅支持数值类型。
LvalueInfo was refactored from separate indices / fields lists to an order-preserving path list (elements are either Integer indices or String fields), faithfully retaining the access order from the source syntax. doAssignment and readLvalue now execute readKey / writeKey in order, enabling deep writes like g[0][0].icon = item ("index-then-field"), while preserving existing behavior for forms like s.name[0] = 'X' ("field-then-index"). String index writes uniformly go through a read–modify–rewrite-parent cycle; compound assignment remains numeric-only.
🐛 Bug Fixes / 缺陷修复 修复了反射调用忽略可变参数导致 addItem 等一批 Bukkit API 无法调用的缺陷;修复了 canConvertOne 对 lambda → 接口的判断未校验形参个数,使得 runTaskLater 在 Runnable 与 Consumer 重载间错误命中后者的缺陷;修复了函数重载注册时仅按参数个数去重,导致 foo(int) 与 foo(string) 被错误判定为冲突的缺陷;修复了 g[0][0].icon 因 LvalueInfo 顺序丢失而被当作"先取 icon 后索引"的缺陷。
Fixed reflection calls ignoring varargs, which prevented APIs such as addItem from being invoked; fixed lambda-to-interface matching in canConvertOne not validating parameter arity, which caused runTaskLater to wrongly bind to the Consumer overload instead of Runnable; fixed function-overload registration deduplicating solely by arity, which falsely rejected foo(int) alongside foo(string); fixed g[0][0].icon being misinterpreted as "fetch icon, then index" due to loss of ordering information in LvalueInfo.
⚠️ Breaking Changes / 兼容性变更 API Warnings使用者提示
runtime.functions 的类型由 Map<String, FunctionDef> 变更为 Map<String, List<FunctionDef>>,直接访问该字段的外部代码需同步调整,建议改用 resolveFunction(name, args) 与 resolveUniqueFunction(name)。EntityRef 现实现 RefLike 接口,其 invoke 签名与原有方法保持一致,但任何按 instanceof EntityRef 分支的外部逻辑应改为 instanceof RefLike。HimDSL.g4 的 unary 规则已全部标签化,visitUnary 被拆分为 visitUnaryOp / visitPreIncrement / visitPreDecrement / visitPostfixUnary,自定义 visitor 需同步适配。
The type of runtime.functions changed from Map<String, FunctionDef> to Map<String, List<FunctionDef>>; external code accessing this field directly must be updated, preferably switching to resolveFunction(name, args) and resolveUniqueFunction(name). EntityRef now implements RefLike; its invoke signature is unchanged, but any external branching on instanceof EntityRef should migrate to instanceof RefLike. The unary rule in HimDSL.g4 is now fully labeled, and visitUnary has been split into visitUnaryOp / visitPreIncrement / visitPreDecrement / visitPostfixUnary; custom visitors must be adapted accordingly.
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle. To learn more about the Modrinth Maven API, click here.
Note: When available, you should use the creator's maven repo instead as it will have transitive dependency information that the Modrinth Maven API does not. You may also end up with duplicate dependencies if you use a mix of Modrinth and non-Modrinth Maven repositories for your dependencies, because the group identifier will be different when served through the Modrinth Maven API.
Maven coordinates:
Version ID:
build.gradle:
repositories {
exclusiveContent {
forRepository {
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
}
}
// forRepositories(fg.repository) // Uncomment when using ForgeGradle
filter {
includeGroup "maven.modrinth"
}
}
}
// Standard Gradle dependency
dependencies {
implementation "maven.modrinth:YUhl4KGE:xVZmzU3n"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:YUhl4KGE:xVZmzU3n"
}

