EntityRef - HimxhMC/HimDSL GitHub Wiki
EntityRef 引用 / EntityRef
引用是什么 / What a Reference Is
选择器、事件参数、GUI 回调拿到的都是引用——一个指向游戏实体的轻量对象。它不是实体本身,而是"通往实体的句柄",通过它读写实体的数据。
Selectors, event parameters, and GUI callbacks all produce references — lightweight objects pointing at game entities. A reference is not the entity, but a "handle" for reading and writing its data.
引用有两种能力:
References have two capabilities:
-
字段 —— 读取实时属性(位置、血量、名字等)。
-
方法 —— 调用实体上的 Bukkit 方法。
-
Fields — read live properties (position, health, name, etc.).
-
Methods — call Bukkit methods on the entity.
常用字段 / Common Fields
用 变量.字段名 访问:
Access with var.fieldName:
var p = @p;
string name = p.name; // 玩家名
string type = p.type; // 实体类型(如 "player"、"zombie")
string world = p.world; // 所在世界名
double x = p.x; // 坐标
double y = p.y;
double z = p.z;
double yaw = p.yaw;
double pitch = p.pitch;
double hp = p.health; // 生命值
int hunger = p.hunger; // 饥饿值
string uuid = p.uuid; // UUID 字符串
int id = p.id; // 实体 ID
字段是实时的。每次访问都会去读游戏当前的值,不是变量捕获时的快照。
Fields are live. Each access reads the entity's current value, not a snapshot.
runcmd("CONSOLE", "say " + p.health);
// 假设这时玩家受伤了
runcmd("CONSOLE", "say " + p.health); // 读到的是新值
字段是只读的 / Fields Are Read-Only
p.name、p.health 等字段不能直接赋值:
Fields like p.name, p.health are read-only:
p.name = "NewName"; // ✗ 报错:Entity 字段是只读的
要修改实体状态,得用方法(见下)。这一点和结构体不同——结构体的字段可写,实体的字段只读。
To change an entity, use methods (below). This differs from structs, whose fields are writable.
调用方法 / Calling Methods
引用上的方法通过反射调用到底层 Bukkit 对象:
Methods on references call through to the underlying Bukkit object:
p.setHealth(20.0);
p.setFoodLevel(20);
p.teleport(new Location(p.world.world(), 0.0, 64.0, 0.0));
p.sendMessage("你好");
方法名、参数类型和 Bukkit API 完全一致。不知道有哪些方法?查 Bukkit / Paper 的文档,或看 IDE 的自动补全(如果环境支持)。
Method names and argument types match the Bukkit API exactly. To find them, consult the Bukkit / Paper docs, or use IDE completion where available.
方法在主线程执行 / Methods Run on the Main Thread
引用上的方法强制在主线程执行。如果你在异步任务(@BukkitRunnable)里调用,HimDSL 会切回主线程执行完再返回。
Methods on references always run on the main thread. If you call them from an async task (@BukkitRunnable), HimDSL switches to the main thread, waits, and returns.
这意味着:
This means:
-
安全:不会因为操作 Bukkit 对象而崩服。
-
有代价:异步里频繁调用会慢。
-
Safe: no crash from Bukkit thread violations.
-
Costly: frequent calls from async are slow.
在异步任务里批量取数据时,先读一次然后缓存:
When reading data in async tasks, read once and cache:
// ✗ 慢:每次访问都切线程
for (int i = 0; i < players.size(); i++) {
if (players.get(i).health > 10) { ... }
}
// ✓ 快:一次切线程读一批
var snapshot = vector();
for (int i = 0; i < players.size(); i++) {
snapshot.add(players.get(i).health);
}
日常脚本在 main 里跑,本身就在主线程,开销可以忽略。
Everyday scripts run in main on the main thread already; the overhead is negligible there.
实体可能失效 / Entities Can Become Invalid
引用持有的实体可能已经不在世界里了(被移除、玩家离线、怪物死亡)。这时访问字段会返回默认值,调用方法会报"实体已失效"。
A reference's entity may already be gone (removed, player disconnected, mob died). Fields then return defaults; methods error with "entity is no longer valid".
长时间持有的引用使用前先检查:
Check long-held references before use:
if (p.isValid()) {
runcmd("CONSOLE", "say " + p.name);
}
isValid() 返回 true 表示实体仍在线。事件里拿到的引用天然有效(事件就是因为它活着才触发),不用每次检查。
isValid() returns true if the entity is still present. References from events are inherently valid (the event fired because it exists), so no check needed there.
引用与 Java 对象 / References and Java Objects
调用返回实体的 Java 方法时,HimDSL 自动包装成引用:
When a Java method returns an entity, HimDSL auto-wraps it:
var target = someMethodReturningEntity();
// target 是引用,可以继续用字段式访问
string name = target.name;
double x = target.x;
反过来,把引用传给期望 Entity 的 Java 方法时,HimDSL 会自动解包:
Conversely, passing a reference where a Java method expects an Entity auto-unwraps:
p.teleport(loc); // p 被解包成 Player 传给 teleport
这层自动转换让引用和 Java 对象之间的衔接非常自然,日常脚本里基本感觉不到接缝。
This auto-conversion makes references and Java objects blend smoothly — rarely noticeable in daily scripts.
为什么优先用引用 / Why Prefer References
后面一章会讲占位符,它也能读实体属性,形如 $player(p, name)$。两者功能有重叠。HimDSL 推荐优先用引用:
A later chapter covers placeholders, which also read entity data as $player(p, name)$. The two overlap. HimDSL recommends references:
-
类型安全:
p.name是字段访问,拼错了报错。$player(p, name)$里name是字符串,拼错返回null。 -
可读:
p.health一眼看出是对p的操作。占位符要读两遍才知道。 -
一致:字段、方法、索引都是同一套语法。占位符另开一套。
-
Type-checked:
p.nameerrors on typos;$player(p, name)$silently returnsnull. -
Readable:
p.healthclearly operates onp. Placeholders take two reads. -
Consistent: fields, methods, and indexing all share one syntax. Placeholders introduce a second.
能写 p.name 就别写 $player(p, name)$。占位符只在一种情况下不可替代:它提供的软依赖功能——例如 $player(p, atroom, "arena1")$ 读取 HimDungeons 的地牢状态——这些是引用上没有的。
Prefer p.name over $player(p, name)$. Placeholders are irreplaceable only for soft-dependency features — e.g. $player(p, atroom, "arena1")$ reading HimDungeons room state — which do not exist on references.
关于占位符的具体内容,下一章统一讲。
Placeholder details are consolidated in the next chapter.
小结 / Summary
-
引用是通往实体的句柄,字段实时、只读。
-
常用字段:
name、type、world、x/y/z、health、hunger、uuid、id。 -
修改用方法(Bukkit API)。
-
引用上的方法在主线程执行,异步场景注意开销。
-
长期持有的引用先
isValid()检查。 -
Java 返回实体自动包成引用,反之自动解包。
-
优先用引用,占位符只在软依赖功能上不可替代。
-
A reference is a handle; fields are live and read-only.
-
Common fields:
name,type,world,x/y/z,health,hunger,uuid,id. -
Modify via methods (Bukkit API).
-
Methods run on the main thread; mind async overhead.
-
Check
isValid()on long-held references. -
Java entities auto-wrap into references and unwrap on the way back.
-
Prefer references; placeholders are only irreplaceable for soft-dependency features.
下一步 / Next Steps
下一章 [占位符参考 / Placeholders Reference] 汇总占位符的语法和常见用法,作为兼容性说明——新代码请优先使用引用。
Next, [占位符参考 / Placeholders Reference] consolidates placeholder syntax and common uses, as a compatibility reference — new code should prefer references.