GUI Refresh - HimxhMC/HimDSL GitHub Wiki

GUI 刷新 / GUI Refresh

为什么需要刷新 / Why Refresh

修改槽位的字段(icon、name、lore)后,改动只存在脚本里。要显示在玩家界面上,必须显式刷新。

Changing slot fields (icon, name, lore) modifies only the script-side data. To display it, you must refresh explicitly.

slot.name = "新名字";           // 只改了脚本数据
// 玩家界面还是旧名字,直到:
gui.update(0, 4);               // 或者 gui.updateAll();

这条规则是 HimDSL 的核心设计。它避免每次字段赋值都触发界面重绘,代价是你要记得刷新。

This is a core HimDSL design. It avoids a redraw per assignment; the cost is you must remember to refresh.


两种刷新方法 / Two Refresh Methods

update(r, c)

刷新单个槽位:

Refresh a single slot:

gui.update(0, 4);               // 刷新第 0 行第 4 列

updateAll()

刷新所有槽位:

Refresh every slot:

gui.updateAll();

性能差异:update 只写一格,updateAll 遍历所有格子。3 行 GUI 有 27 格,updateAll 会写 27 次。对现代服务器来说这点开销可以忽略。

Performance difference: update writes one cell; updateAll writes all. A 3-row GUI has 27 cells; updateAll writes 27 times. Negligible on modern servers.


什么时候用哪个 / Which to Use

默认用 updateAll:

Default to updateAll:

  • 代码简单,不会漏刷新。

  • 建立 GUI 时必须用 updateAll(否则玩家看到空界面)。

  • 动态修改多格时,一次搞定。

  • 性能影响可以忽略。

  • Simple code; nothing forgotten.

  • Required when building the GUI (otherwise empty).

  • One-shot for multi-slot changes.

  • Performance is negligible.

用 update(r, c) 的场景:

Use update(r, c) when:

  • 明确只改了一格。

  • 在一个频繁触发的回调里(例如每秒触发的定时器)。

  • 想让代码"很清楚地指出只改了这一格"。

  • You know exactly one cell changed.

  • Inside a frequently firing callback (e.g. a per-second timer).

  • You want the code to "clearly say only this cell changed".

判断标准:不是"能不用 updateAll 就不用"——大多数时候 updateAll 更清晰,性能差异对玩家完全无感。

Rule of thumb: not "avoid updateAll if possible" — updateAll is clearer most of the time, and players cannot perceive the performance difference.


建立 GUI 时的标准流程 / Standard Flow When Building

int main() {
    var gui = new GUI(3, "菜单");

    // 1. 填入所有槽位
    var slotA = new GUISlot(Material.DIAMOND);
    slotA.name = "§b钻石";
    gui[0][4] = slotA;

    var slotB = new GUISlot(Material.EMERALD);
    slotB.name = "§a绿宝石";
    gui[1][4] = slotB;

    // 2. 一次性刷新
    gui.updateAll();

    // 3. 打开
    gui.open(@p);
}

三步走:填 → 刷新 → 打开。顺序不能颠倒。open 之前必须刷新,否则玩家看到空界面。

Three steps: fill → refresh → open. Order matters. Refresh before open, or the player sees an empty GUI.


动态刷新 / Dynamic Refresh

界面已经打开时,可以在回调里刷新:

While the GUI is open, refresh inside callbacks:

int clicks;

var counterSlot = new GUISlot(Material.CLOCK);
counterSlot.name = "§e点击计数";
counterSlot.lore = {"§7点击次数: §e0"};

counterSlot.onLeftClick = () -> {
    player p = @s;
    clicks += 1;

    counterSlot.lore = {
        "§7点击次数: §e" + clicks
    };

    gui.update(2, 4);           // 只改了这一格
};

每点一次就改一格,用 update(2, 4) 精确刷新。用 updateAll 也可以,只是没必要。

One cell per click, so update(2, 4) is precise. updateAll also works, just unnecessary.


定时器里刷新 / Refreshing from a Timer

定时任务更新界面显示时,刷新会写进所有正在查看该 GUI 的玩家:

When a scheduled task updates the GUI, the refresh affects every viewer:

int seconds;

var timerSlot;

@BukkitRunnable
void tick() sche = 1s {
    seconds += 1;

    if (timerSlot != null) {
        timerSlot.lore = {"§7已运行: §e" + seconds + " 秒"};
        // 需要访问 gui,这里假设 gui 也是全局变量
        // gui.update(1, 4);
    }
}

这个模式适合实时数据展示:在线人数、TPS、倒计时。

This pattern suits live data: online count, TPS, countdowns.


界面已关闭时的刷新 / Refreshing After Close

玩家关闭界面后,gui.update 依然会执行,只是没人能看到。这不会报错,也不占用什么资源。

After the player closes the GUI, gui.update still runs, just invisibly. No error, negligible cost.

@BukkitRunnable
void updater() sche = 5s {
    // 玩家可能已经关闭界面了
    gui.updateAll();                // 安全,无效果
}

不要让刷新成为无意义的后台工作。如果明确知道没有玩家在看这个 GUI,跳过刷新:

Avoid refreshing when no one is watching. Skip when you know it is idle:

@BukkitRunnable
void updater() sche = 5s {
    if (/* 有玩家在看 */) {
        gui.updateAll();
    }
}

判断"有玩家在看"需要自己实现——HimDSL 没有直接的 API。大多数脚本直接刷新,忽略这个小开销。

Checking "is anyone viewing?" requires your own tracking — HimDSL has no direct API. Most scripts just refresh and ignore the tiny overhead.


性能提示 / Performance Notes

刷新对玩家是不可见的(一次 tick 内完成)。不要频繁刷新:

Refresh is invisible to players (done within a tick). Do not refresh too frequently:

  • 每 tick(sche = 1t):极端场景,除非有明确需要,避免。

  • 每秒(sche = 1s):实时数据展示,可以接受。

  • 每 5~30 秒:更常见的节奏。

  • Every tick (sche = 1t): avoid unless clearly needed.

  • Every second (sche = 1s): fine for live data.

  • Every 5–30 seconds: more typical.

刷新频率不要超过数据变化的频率。倒计时每秒变一次,就每秒刷;TPS 每 5 秒变一次有意义,就 5 秒刷。

Do not refresh faster than the data changes. A countdown changes per second — refresh per second. TPS is meaningful on a 5s window — refresh every 5s.


常见错误 / Common Mistakes

错误一:open 前没刷新

gui[0][4] = slot;
gui.open(p);                    // ✗ 空界面

错误二:循环里逐格刷新

for (int r = 0; r < 3; r++) {
    for (int c = 0; c < 9; c++) {
        gui.update(r, c);        // ✗ 应当用 updateAll
    }
}

遍历所有格调 update 等价于 updateAll,但代码更长。

错误三:以为字段改了会自动同步

没有自动同步。改字段 → 手动刷新。这条要牢记。

Mistake 1: Opening without refreshing

Empty GUI.

Mistake 2: Updating in a loop

Just call updateAll.

Mistake 3: Expecting automatic sync

No auto-sync. Change → refresh.


小结 / Summary

  • 修改槽位后必须手动刷新。

  • gui.update(r, c) 刷新一格;gui.updateAll() 刷新全部。

  • 默认用 updateAll。

  • 建立流程:填 → 刷新 → 打开。

  • 定时刷新用于实时数据显示,注意频率。

  • 界面关闭后的刷新无害,但没必要。

  • Manual refresh after modifying slots.

  • gui.update(r, c) for one cell; gui.updateAll() for all.

  • Default to updateAll.

  • Flow: fill → refresh → open.

  • Timer-based refresh for live data; watch frequency.

  • Post-close refresh is harmless but pointless.


GUI 部分回顾 / GUI Review

GUI 部分到此结束。五章覆盖了:创建与打开、槽位与显示、点击回调、关闭回调、刷新策略。

The GUI section ends here. Five chapters covered: creation and opening, slots and display, click callbacks, close callbacks, and refresh strategy.

关键约定回顾:

Key conventions:

  • 新建 GUI → 填槽位 → updateAll → open:顺序固定。

  • @s 在回调里是点击者:这是 GUI 交互的核心。

  • 修改槽位 → 手动刷新:没有自动同步。

  • 每个 GUI 一个 onClose:重复注册会覆盖。

  • New GUI → fill slots → updateAll → open: fixed order.

  • @s in callbacks is the clicker: the heart of GUI interaction.

  • Modify slot → refresh manually: no auto-sync.

  • One onClose per GUI: re-registering overwrites.


下一步 / Next Steps

下一部分进入内置函数参考。第十章 [数学函数 / Math-Builtins] 从最常用的 abs、max、min 讲起。

The next part covers the built-ins reference. Chapter [数学函数 / Math-Builtins] starts with the most common: abs, max, min.