Skip to content

脚本插件(nc) ​

从 JavaScript 脚本迁移过来?

早期的 .js 脚本插件支持已在 1.6.2 移除,.js 文件不再被加载。写法对照见 脚本插件(JavaScript)已移除。

Bngeuit 支持直接用 nc(自研轻量 JVM 语言,nacre 引擎)编写插件:把 .nc 文件放进 plugins/ 目录即自动加载,引擎已随框架主程序打包,无需任何额外安装。

nc 是静态编译到 JVM 字节码的语言(Kotlin 风格语法),脚本在加载时内存编译、直接执行,性能接近原生 Java 插件。

最小示例 ​

nc
package bngeuit.examples.nc

import cn.org.bukkit.bngeuit.Bngeuit
import cn.org.bukkit.bngeuit.event.receive.GroupAtMessageEvent
import cn.org.bukkit.bngeuit.script.NcBridge

// 元信息:pluginName 必填,其余可选
val pluginName = "NcExample"
val pluginVersion = "1.0.0"
val pluginDescription = "nc 脚本插件示例:命令注册 + 事件监听"
val pluginAuthor = "bngeuit"

// 桥接对象:参数必须是本脚本声明的 package
val b = NcBridge.of("bngeuit.examples.nc")

fun onEnable() {
    // 注册命令 /nctest
    b.registerCommand("nctest") { sender, args ->
        sender.sendMessage("你好!这是 nc 脚本插件 " + pluginName + " 的回复")
    }

    // 监听群 @ 消息(lambda 形参自动获得精确事件类型)
    b.on(GroupAtMessageEvent.class) { e ->
        if ("nc你好".equals(e.getContent())) {
            Bngeuit.replyGroupText(e.getGroupOpenid(), e.getId(), 1, "你好,这是来自 nc 脚本插件的回复!")
        }
    }

    b.info("NcExample 已启用(/nctest 命令与群消息监听已就绪)")
}

fun onDisable() {
    b.info("NcExample 已卸载")
}

元信息(顶层 val) ​

nc 是静态语言没有对象字面量,插件元信息用一组顶层 val 声明,字段对应 JAR 插件的 plugin.yml:

顶层 val必填说明
pluginName✅插件名(也用于 plugin load/reload/disable 按名管理)
pluginVersion否版本,默认 1.0
pluginDescription否描述
pluginAuthor否作者
pluginDepend否硬依赖的插件名列表,写 listOf("插件A", "插件B")
pluginSoftdepend否软依赖的插件名列表,写 listOf("插件A", "插件B")

package 约定 ​

nc 强制脚本声明 package(写在文件最前、import 之前),且每个 nc 插件的 package 必须唯一——它同时是桥接对象 NcBridge.of(...) 的查找键。重复声明同一 package 的两个 .nc 文件会拒绝加载并报错。

生命周期 ​

onLoad()      # 加载(脚本已编译、顶层 val 已求值)
    ↓
onEnable()    # 启用(此时注册事件/命令)
    ↓
onActive()    # 机器人上线(两种接入模式统一触发一次)
    ↓
onDisable()   # 卸载(自动注销已注册的事件/命令,释放脚本资源)

四个函数均为可选的无参顶层函数,不定义则跳过。两阶段加载:load → 依赖拓扑排序 → enable。

桥接对象(NcBridge) ​

脚本顶层捕获 val b = NcBridge.of("本脚本的package"),框架交互的统一入口:

API说明
b.on(事件类.class) { e -> ... }注册事件监听(默认优先级 P3、不忽略取消)。lambda 形参自动获得精确事件类型(nc 0.3.2+ 从 Class 实参推断泛型类型变量);也可显式标注 { e: GroupAtMessageEvent -> ... } 或用父类型收窄
b.on(事件类.class, { e -> ... }, EventPriority.P1, true)完整选项:优先级 P1~P6、是否忽略已取消事件
b.on("GroupAtMessageEvent", { e: Event -> ... })按类名注册(全限定名,或内置事件包 event.receive/event.send/event 简名);lambda 参数类型为 Event,访问子类成员需 val gme = e as GroupAtMessageEvent
b.registerCommand("名") { sender, args -> ... }注册命令(简单形式)
b.registerCommand("名", listOf("别名"), "描述", 默认handler, mapOf("子命令" to handler), mapOf())注册命令(完整形式:别名/描述/默认子命令/子命令表/Tab 补全表)
b.runAsync { ... } / runAsyncLater(fn, delayMs) / runAsyncTimer(fn, delayMs, periodMs)异步调度任务。自动以本插件为 owner——插件卸载时由框架统一取消
b.runSync { ... } / runSyncLater(fn, delayMs) / runSyncTimer(fn, delayMs, periodMs)同步调度任务(调度器 sync 单线程串行),owner 语义同上
b.cancelTask(task)取消任务,传入 run* 返回的任务句柄
b.api()获取原始 API(BotApi,上传等门面未覆盖的高级操作)
b.info / warn / error(msg)插件日志
b.getDataFolder()插件数据目录 plugins/<插件名>/

访问 Java 类 ​

nc 直接 import classpath 上的类使用,语法同 Kotlin/Java(构造器、静态成员、实例方法、SAM lambda 桥接均支持):

nc
import java.nio.file.Files
import java.nio.file.Path
import cn.org.bukkit.bngeuit.Bngeuit

fun example() {
    val text = Files.readString(Path.of("config.yml"))
    Bngeuit.sendGroupText("群OpenID", "来自 nc 的消息")
    val t = Thread({ b.info("后台线程运行中") })
    t.start()
}
  • 框架主程序内所有类(Bngeuit 门面、事件、BotApi、调度器等)
  • 字符串模板、is/as、?:、when、data class 等 Kotlin 风格语法均可用,详见 nc 语言文档

访问 JAR 插件类 ​

框架在加载 nc 插件时向 nacre 引擎注入了跨插件类查找:已加载 JAR 插件的类可以直接 import。

nc
import cn.org.bukkit.bngeuit.bridge.BngeuitBridge
import cn.org.bukkit.bngeuit.bridge.event.BridgeMessageReceiveEvent

b.on(BridgeMessageReceiveEvent.class) { e -> ... }

前提与限制:

  • import 的插件必须已加载(声明 pluginDepend = listOf("BngeuitBridge") 保证加载顺序)
  • 不能 import 其他 nc 脚本的类(每个 .nc 的产物类在独立加载器里,不进跨插件注册表)