Skip to content

BukkitRunnable

Himxh edited this page Oct 4, 2026 · 1 revision

@BukkitRunnable

定时任务 / Scheduled Tasks

前面的脚本运行一次就结束。定时任务让代码按固定间隔反复执行:每 10 秒检查一次、每分钟广播一次、每 5 分钟保存一次数据。

Previous scripts ran once. Scheduled tasks run code at fixed intervals: check every 10 seconds, broadcast every minute, save every 5 minutes.

用 @BukkitRunnable 注解标记:

Mark a function with @BukkitRunnable:

@BukkitRunnable
void tick() sche = 10s {
    runcmd("CONSOLE", "say 服务器运行正常");
}

sche = 10s 是调度间隔,表示每 10 秒执行一次。

sche = 10s is the schedule interval: run every 10 seconds.


语法 / Syntax

@BukkitRunnable
返回类型 函数名() sche = 间隔 {
    函数体
}
  • 注解写在最前。

  • 参数列表通常留空(定时任务没有触发者)。

  • 返回类型一般是 void。

  • sche = 间隔 声明周期。

  • @BukkitRunnable 函数也需要 start 才生效。

  • Annotation first.

  • Parameter list is usually empty (no trigger).

  • Return type is normally void.

  • sche = interval declares the period.

  • @BukkitRunnable functions also need start.


时间单位 / Time Units

sche 支持三种后缀:

sche accepts three suffixes:

后缀 含义 例子
s 秒 sche = 5s
t 游戏刻(tick),1 秒 = 20 刻 sche = 100t
ms 毫秒 sche = 500ms
Suffix Meaning Example
s seconds sche = 5s
t ticks (20 per second) sche = 100t
ms milliseconds sche = 500ms

不带后缀时默认为刻:

Without a suffix, ticks are assumed:

sche = 100         // 100 刻 = 5 秒

推荐总是带后缀,5s 比 100t 更好读。

Prefer explicit suffixes; 5s reads better than 100t.


启动与停止 / Starting and Stopping

和事件函数一样,必须显式启动:

Like event handlers, explicitly start them:

@BukkitRunnable
void heartbeat() sche = 30s {
    runcmd("CONSOLE", "say 心跳检测");
}

int main() {
    start("heartbeat");
}

stop("heartbeat") 会取消任务,之后不再执行:

stop("heartbeat") cancels the task:

int main() {
    start("heartbeat");
    // 某个条件下
    // stop("heartbeat");
}

start 一个已经在运行的任务会报错。必须先 stop 再 start。

start on an already-running task errors. stop first, then start.


不带 sche 的一次性任务 / One-Shot Without sche

省略 sche 时,@BukkitRunnable 变成一次性的异步任务,只执行一次:

Omitting sche makes the task a one-shot async task:

@BukkitRunnable
void warmup() {
    runcmd("CONSOLE", "say 延迟初始化完成");
}

int main() {
    start("warmup");
}

区别:

  • 带 sche → 重复执行,主线程,按周期。

  • 不带 sche → 执行一次,异步线程。

  • With sche → repeats on the main thread.

  • Without → runs once on an async thread.

异步意味着不能在任务里直接操作 Bukkit 对象。下一节会讲怎么处理。

Async means you cannot directly touch Bukkit objects. See the next section.


异步注意事项 / Async Considerations

不带 sche 的任务在异步线程执行。异步线程不能直接操作游戏世界——玩家的位置、方块状态、实体属性都只能在主线程改。

One-shot tasks run on an async thread. Async threads cannot directly touch the game world — player positions, block states, entity attributes belong to the main thread.

正确的做法 / Correct Approach

HimDSL 里的引用方法会自动切回主线程,所以以下代码是安全的:

Reference methods auto-switch to the main thread, so this is safe:

@BukkitRunnable
void asyncWork() {
    var players = @a;                // 查询在主线程
    for (int i = 0; i < players.size(); i = i + 1) {
        var p = players.get(i);
        p.sendMessage("异步通知");    // 自动切主线程
    }
}

代价是每次调用都会切线程,异步里频繁操作实体很慢。如果逻辑不涉及游戏对象,异步是高效的——例如写文件、计算。涉及游戏对象时,考虑改用带 sche 的同步任务。

The cost is a thread switch per call. Heavy entity access from async is slow. If logic touches no game objects, async is efficient (file writes, computation). Otherwise prefer a scheduled (main-thread) task.


一个完整例子 / A Complete Example

每隔 5 分钟自动保存一次数据:

Auto-save data every 5 minutes:

@BukkitRunnable
void autosave() sche = 5m {
    // 注意:sche 不支持 "m",用秒代替
}

int main() {
    // 5 分钟 = 300 秒
    start("autosave");
}

等等——sche 不支持分钟单位。5 分钟要写成 300s。上面示例只是示意。

Wait — sche has no minutes unit. Five minutes must be written 300s. The example above is only illustrative.

@BukkitRunnable
void autosave() sche = 300s {
    var lines = vector();
    lines.add("saved at " + $server(tps)$);
    fwritelines("last_save.txt", lines);
    runcmd("CONSOLE", "say 数据已保存");
}

int main() {
    start("autosave");
}

一个常见的坑 / A Common Pitfall

定时任务里不要 sleep:

Do not sleep inside scheduled tasks:

@BukkitRunnable
void bad() sche = 10s {
    runcmd("CONSOLE", "say 开始");
    sleep(5000);                    // ✗ 阻塞主线程 5 秒
    runcmd("CONSOLE", "say 结束");
}

sleep 会阻塞整个服务器,所有玩家都会卡顿。想要"等一段时间再执行下一步",正确做法是拆成多次任务执行,或者用状态变量记住进度。

sleep blocks the whole server. To "wait then continue", split into multiple ticks or track progress in a state variable.


小结 / Summary

  • @BukkitRunnable 标记定时任务。

  • sche = 间隔 声明周期,支持 s、t、ms。

  • 必须 start 才生效。

  • 带 sche 同步重复执行;不带 sche 异步执行一次。

  • 异步里操作实体自动切线程,代价是慢。

  • 定时任务里不要 sleep。

  • 不支持分钟单位,用秒代替。

  • @BukkitRunnable marks scheduled tasks.

  • sche = interval with s, t, ms suffixes.

  • Must start to take effect.

  • With sche: sync repeated. Without: async once.

  • Async entity access auto-switches threads; slower.

  • No sleep inside scheduled tasks.

  • No minutes; use seconds.


下一步 / Next Steps

下一章 start / stop 把事件和定时任务的生命周期管理放在一起讲。

Next, start / stop consolidates lifecycle management for events and scheduled tasks.


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