Repository navigation
Use API wisely
这一章是扩展 API 部分的过滤层。在翻开 HimDSLAPI 之前,先想清楚一件事:
This chapter is the filter layer for the Extension API section. Before opening HimDSLAPI, settle one question:
你到底想让谁调用谁?
Who is calling whom?
方向搞反了,会用错工具,写出一堆不必要的 Java 代码。
Get the direction wrong and you will use the wrong tool, writing Java you did not need.
| 需求 | 需要 API 吗 |
|---|---|
| 脚本要调用其他插件 | 不需要——用脚本自身的 Java 互操作 |
| 其他插件要调用 HimDSL |
需要——这才轮到 HimDSLAPI
|
| Need | API required? |
|---|---|
| Script calls another plugin | No — use the script's Java interop |
| Another plugin calls HimDSL |
Yes — this is HimDSLAPI's territory |
大部分人第一反应是"我想用 HimDSL 做点高级的,肯定要用 API"。但方向往往是反的——真正需要写 Java 的场景,比想象中少得多。
Most people assume "advanced HimDSL = API". Usually the direction is reversed — far fewer cases actually need Java.
这是最常见的需求,也是最不需要 API 的。
This is the most common need — and the one that needs the API least.
HimDSL 脚本本身就是 Java 互操作的入口。任何 public 的类、方法、字段,脚本都能直接访问。不用写一行 Java 代码。
A HimDSL script is itself a Java interop entry point. Any public class, method, or field is directly reachable. No Java code required.
// 创建其他插件的对象
var holder = new com.example.myplugin.DataHolder("steve", 5);
var config = new com.example.myplugin.MyConfig();
只要类名写全,不需要任何注册或声明。
Write the full class name; no registration or declaration needed.
// 调用其他插件的静态工具方法
string formatted = com.example.myplugin.Utils.formatName("steve");
int result = com.example.myplugin.MathUtil.add(3, 7);
规则和调用 Bukkit 的静态方法完全相同——写全类名,点号,方法名,参数。
Identical to calling Bukkit statics — full class name, dot, method, arguments.
// 读其他插件的静态常量
int maxLevel = com.example.myplugin.Constants.MAX_LEVEL;
string prefix = com.example.myplugin.Constants.PREFIX;
int main() {
// 假设某插件提供静态工具
int score = com.example.questplugin.QuestAPI.getScore(@p.uuid);
// 假设某插件提供可 new 的对象
var board = new com.example.scoreboard.ScoreboardBuilder();
board.setTitle("积分榜");
board.setLine(0, "你的分数: " + score);
// 通过反射调用实例方法
board.build();
}
整个脚本没有一行 Java,但完整地用上了其他插件的能力。
Zero Java written, yet the other plugin's capabilities are fully used.
三种情况:
Three cases:
Java 的访问修饰符依然生效。private、包级私有、protected 的类和方法访问不到。
Java's access modifiers still apply. private, package-private, and protected members are unreachable.
如果插件的 API 是"你实现一个接口,我稍后调用"的模式——比如注册事件监听器——脚本做不到。因为脚本没有"实现 Java 接口"的语法。
If the API follows "implement an interface, I call you later" — like registering a listener — the script cannot do it. Scripts have no syntax for implementing a Java interface.
例外:函数式接口例外。单个抽象方法的接口可以用 lambda 适配(见《函数式接口》)。
Exception: functional interfaces. Single-abstract-method interfaces adapt via lambda (see Functional Interfaces).
有些 API 是实例方法——你先要拿到插件对象:
Some APIs are instance methods — you first need the plugin object:
MyPlugin plugin = (MyPlugin) Bukkit.getPluginManager().getPlugin("MyPlugin");
plugin.doSomething();HimDSL 脚本能做到:
A HimDSL script can:
var plugin = Bukkit.getPluginManager().getPlugin("MyPlugin");
plugin.doSomething();
如果 getPlugin 返回的是接口,方法调用会走到反射,正常工作。如果能拿到实例,这条路就通。
If getPlugin returns an interface, reflection resolves the method. If you can reach the instance, this path works.
这才是 HimDSLAPI 真正存在的理由。
This is why HimDSLAPI exists.
触发时机不在脚本控制范围内时——比如"另一个插件在它的某个流程里,要执行一段 HimDSL 逻辑"——脚本自己无法感知。
When the trigger is outside the script's control — "another plugin, inside its own flow, wants a HimDSL script to run" — the script cannot sense this.
HimDSL 脚本只能被:
A HimDSL script can only be driven by:
-
Bukkit 事件(
@EventHandler)。 -
定时任务(
@BukkitRunnable)。 - GUI 交互。
-
/himdsl run命令。 -
服务器启动 / 关闭(
onServerLoad.himdsl等)。
如果触发源不在这份清单里,就得让对方插件来通知 HimDSL。这需要 Java 代码,需要 HimDSLAPI。
If the trigger is not in this list, the other plugin must notify HimDSL — Java code, HimDSLAPI.
用下面三个问题判断:
Three questions:
-
触发时机是脚本能感知的吗?
- 是(Bukkit 事件、定时器、GUI、命令)→ 不用 API。
- 否 → 继续第 2 步。
-
触发时机是否可以改成"定时轮询"?
- 可以(例如"每 5 秒查一次"能接受)→ 不用 API,写个
@BukkitRunnable。 - 不可以(必须实时响应)→ 继续第 3 步。
- 可以(例如"每 5 秒查一次"能接受)→ 不用 API,写个
-
能否让对方插件通过 PlaceholderAPI 暴露状态?
- 可以 → 不用 API,脚本用
papi(...)读。 - 不可以(对方不提供 PAPI 扩展)→ 需要 API。
- 可以 → 不用 API,脚本用
-
Is the trigger something the script can sense?
- Yes (Bukkit event, timer, GUI, command) → No API.
- No → step 2.
-
Can the trigger be turned into "polling"?
- Yes ("check every 5 s" is acceptable) → No API; write a
@BukkitRunnable. - No (must be real-time) → step 3.
- Yes ("check every 5 s" is acceptable) → No API; write a
-
Can the other plugin expose state via PlaceholderAPI?
- Yes → No API; read with
papi(...). - No → API is justified.
- Yes → No API; read with
只有同时满足这三个条件:
Only when all three hold:
-
触发时机不在 Bukkit 事件里。
-
不能延迟(轮询不可接受)。
-
对方插件不提供 PlaceholderAPI 扩展。
-
The trigger is not a Bukkit event.
-
No delay is acceptable (polling ruled out).
-
The other plugin has no PlaceholderAPI extension.
典型例子:
Typical examples:
-
你的插件在自定义流程的中间要"询问 HimDSL"。
-
你的插件动态生成脚本内容(用户在 GUI 里配规则,你翻译成脚本)。
-
你的插件在服务器启动之前就要写入脚本。
-
Your plugin needs to "ask HimDSL" mid-flow.
-
Your plugin dynamically generates script content (GUI rules → script).
-
Your plugin writes scripts before server startup.
其他所有场景,都能用脚本自身的 Java 互操作、PlaceholderAPI、或普通事件系统解决。
Every other case is solvable with the script's Java interop, PlaceholderAPI, or the plain event system.
想做的事:脚本里读取某插件的玩家分数。
Goal: read a player's score from another plugin, inside a script.
- 写一个 Java 插件。
- 用
HimDSLAPI.registerPlaceholder覆盖一个内置类型。 - 脚本里调
$player(score)$。 - 影响所有脚本的
$player$行为。
几十行 Java,破坏性副作用。
Dozens of lines of Java, destructive side effects.
int score = com.example.questplugin.QuestAPI.getScore(@p.uuid);
一行脚本,零副作用。
One line, zero side effects.
这个对比就是本章的全部要义。
This comparison is the whole point of this chapter.
不成立。HimDSL 脚本本身就能访问整个 Java 生态。用全限定名 + new + 静态方法,已经覆盖绝大多数需求。
False. HimDSL scripts reach the entire Java ecosystem. FQNs + new + static methods cover most needs.
看方向。脚本调其他插件 = 用脚本语法。其他插件调脚本 = 用 API。方向反了,写再多代码也是错的。
Direction matters. Script → plugin = script syntax. Plugin → script = API. Reversed direction means wrong code.
恰恰相反。PAPI 扩展跨版本稳定,对脚本用户透明,不影响其他插件。它比 HimDSLAPI 更适合跨插件集成。
Opposite. PAPI extensions are version-stable, transparent to scripts, non-invasive. They beat HimDSLAPI for cross-plugin integration.
看情况。脚本调一次静态方法的开销和 Java 调用同一量级(都走反射,HimDSL 已经缓存)。唯一"更高效"的场景是"每秒调用上万次"——这在服务器脚本里几乎不存在。
Depends. A single static call from a script costs the same order as Java (reflection, cached by HimDSL). "More efficient" only matters at ten-thousands-per-second rates — rare in server scripts.
-
先问方向:脚本调其他插件,还是其他插件调脚本?
-
脚本调其他插件 → 用全限定名 +
new+ 静态方法。不需要 API。 -
其他插件调脚本 → 才轮到
HimDSLAPI。 -
判断标准:触发时机是否在脚本控制内?能否改成轮询?对方是否提供 PAPI?
-
API 只在三种情况同时满足时才必要。
-
不要为了"看起来正规"而写不必要的 Java。
-
Ask direction first: script → plugin, or plugin → script?
-
Script → plugin → FQN +
new+ statics. No API needed. -
Plugin → script →
HimDSLAPI's territory. -
Decision: is the trigger script-sensible? Can it become polling? Does PAPI exist?
-
The API is needed only when all three conditions hold.
-
Do not write Java for the appearance of rigor.
如果你确认了方向是"其他插件调 HimDSL",翻到 HimDSLAPI。
If you have confirmed the direction is "plugin → script", turn to HimDSLAPI.
如果确认方向是"脚本调其他插件",回到 [Java 互操作 / Java-Interop],那里讲了 new、静态方法、全限定名的完整用法。
If the direction is "script → plugin", return to [Java 互操作 / Java-Interop] for full usage of new, statics, and FQNs.
(第五十二章 · 前置章完 / End of Prerequisite Chapter 52)
- 首页 / Home
- 安装与快速开始 / Installation & Quick Start
- 第一个脚本 / Your First Script
- 命令速查 / Command Reference
- 自动执行脚本 / Auto-run Scripts
- 定义函数 / Defining Functions
- 函数重载 / Function Overloading
- Lambda 表达式 / Lambda Expressions
- 函数式接口 / Functional Interfaces
- 选择器总览 / Selectors Overview
- 选择器过滤器 / Selector Filters
- EntityRef 引用 / EntityRef
- 占位符参考 / Placeholders Reference
- PlaceholderAPI 桥接 / PAPI Bridge
- 创建 GUI / Creating a GUI
- GUISlot 槽位 / GUISlot
- 点击回调 / Click Callbacks
- onClose 回调 / onClose Callback
- GUI 刷新 / GUI Refresh
- 数学函数 / Math Built-ins
- 高精度计算 / BigDecimal Built-ins
- 字符串函数 / String Built-ins
- 集合函数 / Collection Built-ins
- 文件 I/O / File I/O
- 命令与 sleep / Commands & sleep