Repository navigation
Custom Placeholders
其他插件可以通过 HimDSL 的 API 注册自定义占位符,让脚本里能读取你的插件数据。
Other plugins may register custom placeholders through HimDSL's API so scripts can read your plugin's data.
典型的集成场景:
Typical integrations:
-
你的插件管理"领地"或"房间",脚本要读玩家是否在其中。
-
你的插件维护"分数"或"等级",脚本要根据分数发放奖励。
-
你的插件有"队伍"信息,脚本要显示队伍状态。
-
Your plugin manages regions or rooms; scripts need to know if a player is inside.
-
Your plugin tracks scores or levels; scripts grant rewards based on them.
-
Your plugin has teams; scripts display team status.
注意:这一章面向 Java 开发者。脚本用户看到的是 $你的类型(...)$ 这样的调用,不知道背后的实现。
Note: this chapter is for Java developers. Script authors see $yourType(...)$ calls without knowing the implementation.
package com.himdsl.api;
@FunctionalInterface
public interface PlaceholderHandler {
Object resolve(List<Object> args);
}函数式接口,一个方法。args 是脚本传入的参数列表,已经求值——里面是数字、字符串、引用等实际对象,不是语法文本。
A functional interface, one method. args is the argument list from the script, already evaluated — numbers, strings, references, not syntax text.
返回值可以是任意类型,脚本拿到的是这个对象的直接引用。返回 null 表示"没有值"。
The return value may be any type; the script receives the object directly. null means "no value".
import com.himdsl.api.HimDSLAPI;
import com.himdsl.api.PlaceholderHandler;
HimDSLAPI api = Bukkit.getServicesManager().load(HimDSLAPI.class);
api.registerPlaceholder("mytype", args -> {
if (args.isEmpty()) return null;
String key = args.get(0).toString();
switch (key) {
case "score": return getScore();
case "team": return getTeam();
default: return null;
}
});脚本中这样使用:
In a script:
int s = $mytype(score)$;
string t = $mytype(team)$;
这是本章最重要的部分。
The most important part of this chapter.
HimDSL 的语法只接受五种占位符类型名:
HimDSL's grammar accepts only five placeholder type names:
player / p
world / w
server / s
boss / b
haveVar
自定义占位符的注册名必须是这五个之一。上面的 mytype 示例无法在脚本里写出来——$mytype(...)$ 会直接解析失败。
Custom placeholder names must be one of these five. The mytype example above cannot be typed in a script — $mytype(...)$ fails to parse.
为什么? 占位符的类型名在语法层面是关键字,不是普通标识符。$mytype(...)$ 中的 mytype 无法被语法识别为占位符类型,因此报"语法错误"。
Why? Placeholder type names are keywords at the grammar level, not ordinary identifiers. mytype inside $mytype(...)$ is not recognized as a placeholder type and errors.
实际用法:注册一个覆盖内置类型的 handler,改变这个类型的解析行为:
Practical use: register a handler that overrides a built-in type, changing its resolution:
// 覆盖 $player$ 的行为
api.registerPlaceholder("player", args -> {
// 你的逻辑
});但这会替换 HimDSL 内置的 player 解析——脚本里所有 $player(...)$ 都会走你的 handler,包括那些原本应该读玩家属性的调用。这几乎肯定不是你想要的。
But this replaces HimDSL's built-in player resolution — every $player(...)$ in every script goes through your handler, including calls meant to read player attributes. Almost certainly not what you want.
坦率地说,这个功能的实用性有限。它设计出来的场景可能是"多个插件共享占位符命名空间",但语法限制让这条路很窄。
Honestly, this feature has limited practical value. It may have been designed for "multiple plugins sharing a placeholder namespace", but the grammar restricts that path.
实际上更实用的集成方式是PlaceholderAPI——通过它注册 %yourplugin_key%,脚本里用 papi("yourplugin_key") 读取。这不受 HimDSL 语法限制,也不影响内置占位符。
The practical alternative is PlaceholderAPI — register %yourplugin_key%, and scripts read it via papi("yourplugin_key"). This is not restricted by HimDSL's grammar and does not touch built-in placeholders.
建议:
Recommendation:
-
优先通过 PlaceholderAPI 暴露数据。
-
不要用
registerPlaceholder覆盖内置类型。 -
如果你的插件和 HimDSL 属于同一套系统(例如 HimDSL 是某个大插件的子系统),可以考虑覆盖
boss或server这种小众类型。 -
其他情况下,别用这个 API。
-
Expose data through PlaceholderAPI when possible.
-
Do not use
registerPlaceholderto override built-in types. -
If your plugin and HimDSL belong to the same suite (HimDSL is a subsystem of a larger plugin), overriding niche types like
bossorservermight make sense. -
Otherwise, skip this API.
推荐的方式:
The recommended way:
public class MyPlugin extends JavaPlugin implements Listener {
@Override
public void onEnable() {
if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) {
new MyExpansion().register();
}
}
class MyExpansion extends PlaceholderExpansion {
@Override public String getIdentifier() { return "myplugin"; }
@Override public String getAuthor() { return "me"; }
@Override public String getVersion() { return "1.0"; }
@Override
public String onRequest(OfflinePlayer p, String params) {
if (params.equals("score")) return String.valueOf(getScore(p));
if (params.equals("team")) return getTeam(p);
return null;
}
}
}脚本中:
In a script:
var p = @p;
string score = papi("myplugin_score", p);
int s = toInt(score);
这种方式不侵入 HimDSL 的内部机制,两个系统各自独立。
This approach does not intrude into HimDSL's internals; the two systems stay independent.
参数:脚本传入的参数已经求值。第一个参数是变量名(字符串形式),后续参数是普通表达式。
Arguments: passed already evaluated. The first is a variable name (as a string); the rest are ordinary expressions.
api.registerPlaceholder("server", args -> {
// 脚本 $server(tps)$ 里,args = ["tps"]
// 脚本 $server(ping, "Steve") 里,args = ["ping", "Steve"]
if (args.size() > 1) {
return args.get(1); // 拿到 "Steve"
}
return null;
});返回值:任意对象。脚本直接拿到这个对象,可以当数字、字符串、列表、引用使用。返回 null 时脚本读到 null。
Returns: any object. The script receives it directly as a number, string, list, or reference. null gives the script null.
不要返回 Bukkit 的 Entity 或复杂对象,除非你明确知道脚本会怎么用。简单类型(数字、字符串、布尔)最安全。
Avoid returning Bukkit Entity or complex objects unless you know how the script uses them. Simple types (numbers, strings, booleans) are safest.
注册是一次性的。HimDSL 加载时清空所有自定义占位符——也就是说每次 HimDSL 重载,你的注册都会丢失。
Registration is one-shot. HimDSL clears all custom placeholders on load — your registration is lost on every HimDSL reload.
如果你的插件和 HimDSL 同时加载,通常在 onEnable 里注册一次就够了。但如果用户重载 HimDSL,需要重新注册。可以监听 ServerLoadEvent 或 HimDSL 的加载事件。
If both plugins load together, onEnable registration suffices. If the user reloads HimDSL, re-register. Listen for ServerLoadEvent or similar.
实际上,重载场景非常罕见,不值得为此增加复杂度。大多数插件注册一次就够。
In practice, reloads are rare and not worth complicating for. Most plugins register once.
-
PlaceholderHandler是函数式接口,resolve(args)返回任意对象。 -
参数已求值,第一个通常是变量名。
-
重要限制:注册名必须是
player、world、server、boss、haveVar之一。 -
覆盖内置类型会影响所有脚本,谨慎使用。
-
推荐用 PlaceholderAPI 集成,不受语法限制。
-
注册在 HimDSL 重载时会丢失,日常场景不用管。
-
返回简单类型最安全。
-
PlaceholderHandleris a functional interface;resolve(args)returns any object. -
Arguments are evaluated; the first is usually a variable name.
-
Key limit: the name must be
player,world,server,boss, orhaveVar. -
Overriding built-ins affects all scripts. Use with care.
-
Prefer PlaceholderAPI integration — no grammar restrictions.
-
Registration is lost on HimDSL reload; usually a non-issue.
-
Return simple types for safety.
下一章 [嵌入式调用 / Embedding] 汇总跨插件集成的最佳实践。
Next, [嵌入式调用 / Embedding] summarizes best practices for cross-plugin integration.
- 首页 / 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