Skip to content

Custom Placeholders

Himxh edited this page Oct 4, 2026 · 1 revision

自定义占位符 / Custom Placeholders

用途 / Purpose

其他插件可以通过 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.


接口 / The Interface

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".


注册 / Registering

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)$;

重要限制:类型名只能覆盖内置五种 / Important Limitation: Only Five Type Names

这是本章最重要的部分。

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.


那么,自定义占位符还有用吗 / So, Is This Feature Useful?

坦率地说,这个功能的实用性有限。它设计出来的场景可能是"多个插件共享占位符命名空间",但语法限制让这条路很窄。

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:

  1. 优先通过 PlaceholderAPI 暴露数据。

  2. 不要用 registerPlaceholder 覆盖内置类型。

  3. 如果你的插件和 HimDSL 属于同一套系统(例如 HimDSL 是某个大插件的子系统),可以考虑覆盖 boss 或 server 这种小众类型。

  4. 其他情况下,别用这个 API。

  5. Expose data through PlaceholderAPI when possible.

  6. Do not use registerPlaceholder to override built-in types.

  7. If your plugin and HimDSL belong to the same suite (HimDSL is a subsystem of a larger plugin), overriding niche types like boss or server might make sense.

  8. Otherwise, skip this API.


示例:通过 PlaceholderAPI 集成 / Example: PlaceholderAPI Integration

推荐的方式:

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 and Returns

参数:脚本传入的参数已经求值。第一个参数是变量名(字符串形式),后续参数是普通表达式。

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.


生命周期 / Lifecycle

注册是一次性的。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.


小结 / Summary

  • PlaceholderHandler 是函数式接口,resolve(args) 返回任意对象。

  • 参数已求值,第一个通常是变量名。

  • 重要限制:注册名必须是 player、world、server、boss、haveVar 之一。

  • 覆盖内置类型会影响所有脚本,谨慎使用。

  • 推荐用 PlaceholderAPI 集成,不受语法限制。

  • 注册在 HimDSL 重载时会丢失,日常场景不用管。

  • 返回简单类型最安全。

  • PlaceholderHandler is 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, or haveVar.

  • 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.


下一步 / Next Steps

下一章 [嵌入式调用 / Embedding] 汇总跨插件集成的最佳实践。

Next, [嵌入式调用 / Embedding] summarizes best practices for cross-plugin integration.


HimDSL 文档 / Documentation

入门 / Getting Started

语言基础 / Language Basics

控制流 / Control Flow

函数 / Functions

数据结构 / Data Structures

自定义类型 / Custom Types

Minecraft 集成 / Minecraft Integration

事件与调度 / Events & Scheduling

GUI 系统 / GUI System

内置函数参考 / Built-ins Reference

进阶 / Advanced

扩展 API / Extension API

你真的需要 API 吗?/ Do you really need the API?

Clone this wiki locally