Use API wisely - HimxhMC/HimDSL GitHub Wiki
真的需要 API 吗? / Do You Really Need the API?
定位 / Positioning
这一章是扩展 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.
两个方向 / Two Directions
| 需求 | 需要 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.
场景一:脚本调用其他插件 / Scenario 1: Script Calls Another Plugin
这是最常见的需求,也是最不需要 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.
用全限定名 new 对象 / Use the FQN to new an Object
// 创建其他插件的对象
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.
直接调用静态方法 / Call Static Methods Directly
// 调用其他插件的静态工具方法
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.
直接读静态字段 / Read Static Fields Directly
// 读其他插件的静态常量
int maxLevel = com.example.myplugin.Constants.MAX_LEVEL;
string prefix = com.example.myplugin.Constants.PREFIX;
举个完整例子 / A Complete Example
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.
什么时候这个方法会失效 / When This Fails
三种情况:
Three cases:
情况一:类不是 public / Not public
Java 的访问修饰符依然生效。private、包级私有、protected 的类和方法访问不到。
Java's access modifiers still apply. private, package-private, and protected members are unreachable.
情况二:需要注册回调 / Callback Registration Required
如果插件的 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).
情况三:需要拿到插件的实例 / You Need the Plugin Instance
有些 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.
场景二:其他插件调用 HimDSL / Scenario 2: Another Plugin Calls HimDSL
这才是 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.
判断标准 / Decision Rule
用下面三个问题判断:
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
API 真正必要的情况 / When the API Is Truly Needed
只有同时满足这三个条件:
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.
一个反面教材 / A Counter-Example
想做的事:脚本里读取某插件的玩家分数。
Goal: read a player's score from another plugin, inside a script.
用 API 的做法(过度) / The Over-Engineered Way
- 写一个 Java 插件。
- 用
HimDSLAPI.registerPlaceholder覆盖一个内置类型。 - 脚本里调
$player(score)$。 - 影响所有脚本的
$player$行为。
几十行 Java,破坏性副作用。
Dozens of lines of Java, destructive side effects.
直接用引用的做法(正确) / The Right Way
int score = com.example.questplugin.QuestAPI.getScore(@p.uuid);
一行脚本,零副作用。
One line, zero side effects.
这个对比就是本章的全部要义。
This comparison is the whole point of this chapter.
常见误解 / Common Misunderstandings
误解一:"高级功能需要 API" / "Advanced = API"
不成立。HimDSL 脚本本身就能访问整个 Java 生态。用全限定名 + new + 静态方法,已经覆盖绝大多数需求。
False. HimDSL scripts reach the entire Java ecosystem. FQNs + new + static methods cover most needs.
误解二:"用 API 更正规" / "API Looks More Formal"
看方向。脚本调其他插件 = 用脚本语法。其他插件调脚本 = 用 API。方向反了,写再多代码也是错的。
Direction matters. Script → plugin = script syntax. Plugin → script = API. Reversed direction means wrong code.
误解三:"PlaceholderAPI 太麻烦,不如直接调 API" / "PAPI Is a Hassle"
恰恰相反。PAPI 扩展跨版本稳定,对脚本用户透明,不影响其他插件。它比 HimDSLAPI 更适合跨插件集成。
Opposite. PAPI extensions are version-stable, transparent to scripts, non-invasive. They beat HimDSLAPI for cross-plugin integration.
误解四:"写 Java 更高效" / "Java Is Faster"
看情况。脚本调一次静态方法的开销和 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.
小结 / Summary
-
先问方向:脚本调其他插件,还是其他插件调脚本?
-
脚本调其他插件 → 用全限定名 +
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.
下一步 / Next Steps
如果你确认了方向是"其他插件调 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)