dnf/v1
接口手册 / dnf/v1

Lua API 参考

逐项查阅框架、事件、Hook、配置与公告 API 的签名及约束。

接口总览

本页列出当前 dnf/v1 的全部公开函数和数据入口。脚本以 local dnf = require("dnf") 获取宿主表;框架和插件得到的表不同。事件 payload 见完整事件目录,每项规则输入见 Hook 详解。

接口 使用位置 返回值
require("dnf") 框架、插件 当前环境的 dnf 表;其他模块名报错
dnf.api_version 框架、插件 字符串 "dnf/v1"
dnf.config / ctx.config 插件 / 回调 当前 VM 的配置表
dnf.plugins.load(path, options) 框架 无返回值
dnf.plugins.clear_announcement(mode) 框架 无返回值
dnf.events.on(name, handler) 插件初始化 无返回值
dnf.events.catalog() 插件 名称到英文语义的独立副本
dnf.hooks.catalog() 插件 扩展点到 {permission, fields, failure} 的独立副本
dnf.hooks.before(point, handler) 插件初始化 无返回值;回调必须返回 boolean
dnf.providers.register(point, handler) 插件初始化 无返回值;回调必须返回补丁表
dnf.hooks.transform(point, handler) 插件初始化 无返回值;回调返回补丁表或 nil
ctx.notice.broadcast(request) / dnf.notice.broadcast(request) 有权限的通知回调 true 表示加入本批暂存;错误抛异常

当前没有文件、数据库、计时器、日志输出、任意改属性或直接发物接口。不要把运行环境以外的 Lua 标准库或客户端 DLL 能力当成宿主 API。

框架:声明插件集合

dnf.plugins.load

lua
local dnf = require("dnf")

dnf.plugins.load("plugins/community-welcome", {
    enabled = true,
    permissions = { "notice.broadcast" },
    config = {
        message = "欢迎来到服务器",
        settings = { channel_only = true }
    }
})
参数 类型与约束
path 相对顶层 runtime/ 的目录;使用正斜线,位于 plugins/ 或 assets/plugins/ 下
options.enabled 必须显式填写 boolean;省略、1、"true" 均不合法
options.permissions 可选连续字符串数组,值见权限表;最多 16 项
options.config 可选字符串键配置表,嵌套表同样只能用字符串键;不能用 Lua 数组

只有 enabled、permissions、config 三种选项。未知选项报错。enabled=false 的声明不读取该插件的清单或源码,也不监测目录里的文件;禁用占位目录可以暂不存在。最多 128 次声明、16 个启用插件,启用路径和清单 ID 不得重复。

框架构造的是下一代完整集合;调用 load 不会立刻改动正在运行的版本。声明顺序决定同阶段 Hook 顺序。添加、删去或禁用任一插件,下一次成功重载都会重建整个集合。

配置集合应写成字符串键映射:

lua
-- 正确:可用于按角色 ID 查表。
config = { blocked_ids = { ["123"] = true, ["456"] = true } }

-- 错误:config 内不接受数字键数组。
-- config = { blocked_ids = { "123", "456" } }

dnf.plugins.clear_announcement

lua
dnf.plugins.clear_announcement("auto")
模式 行为
auto 默认。存在唯一 dungeon.clear_announcement provider 时由插件负责,否则用内置播报
builtin 用原内置播报,该点的 before/provider/transform 均不执行;普通 dungeon.cleared 订阅仍执行
plugin 必须存在唯一播报 provider,否则整个候选集合被拒绝;插件播报失败不补发内置公告

即使选择 builtin,同一个扩展点存在多个 provider 仍会导致候选校验失败。

清单:plugin.json

json
{
  "id": "community-welcome",
  "version": "1.0.0",
  "api": "dnf/v1",
  "entry": "main.lua",
  "permissions": ["notice.broadcast"]
}
字段 要求
id 匹配 ^[a-z][a-z0-9-]{0,63}$;同一集合内唯一
version 非空字符串;推荐语义化版本以便人工识别,宿主当前不校验 SemVer,也不以它决定重载
api 必须为 "dnf/v1"
entry 必须为 "main.lua",当前不能自定义入口名
permissions 可选字符串数组;必须同时获得框架授权

未知字段、多个 JSON 值、重复 ID、不兼容 API、未授权权限等会拒绝整个候选集合。JSON 不支持注释或尾随逗号。

权限必须两边一致

插件清单申请权限,框架明确授予权限,两者共同决定实际权限。框架授予但插件未申请,也不会自动生效。只订阅事件或读取自身配置不需要额外权限。

权限 允许的操作
notice.broadcast 通知回调暂存公告,以及注册 dungeon.clear_announcement 的任一阶段
progression.clear_experience 注册基础通关经验 Hook 的任一阶段
inventory.item_use 注册已接入 op27 道具使用限制 Hook
commerce.shop_purchase 注册 NPC、秘密商店、Cera 购买限制 Hook
dungeon.admission 注册已接入首次副本入场限制 Hook
chat.message 注册聊天限制 Hook

没有名为 dungeon.clear_announcement 的权限;该点使用 notice.broadcast。权限也不会赋予访问会话、事务、数据库或原始协议对象的能力。

配置:dnf.config 与 ctx.config

dnf.config 来自框架本次 load 的 config。回调里的 ctx.config 与当前 VM 的 dnf.config 是同一张表。未提供配置时得到空表。

lua
local dnf = require("dnf")
assert(type(dnf.config.message) == "string", "config.message must be a string")

dnf.events.on("character.bound", function(ctx, event)
    local message = ctx.config.message
    -- 使用 message;可用广播示例见下方。
end)

改表不会写回框架文件,也不会同步到其他插件或同一插件的另一个 VM。推荐初始化时用 assert 尽早验证配置,让错误候选被拒绝。配置只支持有限数字、布尔、字符串、字符串键嵌套表;容量见运行预算。

事件:dnf.events.on

lua
dnf.events.on("character.bound", function(ctx, event)
    local id = event.data.id
    local channel_id = event.data.channel_id
end)

仅初始化时注册,回调返回值被忽略。每插件最多 64 个通知回调。通知统一接收信封:

lua
-- 结构示意,值为示例。
event = {
    id = "event-123",
    name = "character.bound",
    version = 1,
    occurred_at_ms = 1700000000000,
    data = { id = "123", channel_id = 1 }
}

id 是进程内追踪标识,不能用作跨重启奖励幂等键;occurred_at_ms 是 Unix 毫秒。大多数角色事件包含 data.character={id:string,channel_id:integer},但 character.bound / unbound 直接把 id、channel_id 放在 data,不能套用同一层级。

名称与通配

订阅形式 例子 含义
稳定精确名称 inventory.operation_applied 只匹配这一事件
已知前缀通配 inventory.*、chat.message.* 必须以 .* 结尾,且前缀覆盖至少一个稳定事件
全局 * 匹配所有已发布事件,含诊断事件
game 领域 game.* 匹配稳定 game.operation.finished 和游戏诊断事件
诊断精确名称 game.game-dungeon-cycle-clear-deferred 必须以 game.game- 开头、不含通配且不超过 160 字节;拼错可能注册成功但永不触发

inventory.*.changed、未知领域 inventroy.* 不合法。对于同一事件,顺序是精确名、最长前缀、更短前缀、*,各组内按注册顺序。例如 chat.message.received → chat.message.* → chat.* → *。

同一插件匹配多个组只入队一次,但所有匹配回调都执行,共享该事件 10ms 和 4 条公告预算。每个回调获得独立事件快照,改动不回写核心或后续回调。通知尽力交付,具体提交、回包和去重边界必须查事件目录。

能力发现:两个 catalog

lua
local events = dnf.events.catalog()
assert(events["commerce.shop_purchased"] ~= nil)

local hooks = dnf.hooks.catalog()
assert(hooks["commerce.shop_purchase"].permission == "commerce.shop_purchase")
-- hooks[point].fields 是允许字段的数组。
-- hooks[point].failure 是英文失败语义说明。

events.catalog() 返回 名称 -> 英文语义;hooks.catalog() 返回 扩展点 -> {permission,fields,failure}。两个结果均为深拷贝,修改不会修改宿主能力或授予权限。

这两个函数可供插件或开发工具确认当前版本是否具备所需能力。管理端的 GET /local/plugins/catalog 返回同源目录,包含 api、events、hooks。

规则注册:before、provider、transform

lua
dnf.hooks.before("inventory.item_use", function(ctx, plan, event)
    return true
end)

dnf.providers.register("inventory.item_use", function(ctx, plan, event)
    return { allowed = true }
end)

dnf.hooks.transform("inventory.item_use", function(ctx, plan, event)
    return nil
end)

上述示意需申请并获授 inventory.item_use 权限。只在初始化注册;每插件每点每阶段最多一个回调,整个集合每点最多一个 provider。

顺序为全部 before → 唯一 provider → 全部 transform,同阶段按框架加载顺序。每个回调得到当前 plan、原 event 的新快照。

阶段 回调返回值 效果
before 必须 boolean true 继续;false 设置 enabled=false 并停止后续回调
provider 必须字符串键补丁表 合并返回字段;省略字段保留当前值
transform 字符串键补丁表或 nil 合并返回字段;nil 保留当前方案

直接 plan.allowed=false 然后 return nil 不会生效,必须 return {allowed=false}。未知字段、错误类型、超范围或非整数数值等导致整链失败,不输出先前已经合并的部分结果。补丁中的 nil 不会成为删除指令。

enabled=false 后剩余回调不执行;四项业务限制 Hook 的 allowed=false 也停止后续执行,其他插件不能重新放行。各 Hook 在取消、错误和超时后的处理不同,务必阅读 Hook 详解。

公告:notice.broadcast

lua
local dnf = require("dnf")

dnf.events.on("character.bound", function(ctx, event)
    ctx.notice.broadcast({
        text = "欢迎,角色 " .. event.data.id,
        scope = "channel",
        channel_id = event.data.channel_id,
        color = 0xFFFFD54F,
        surface = "native_chat"
    })
end)

此代码要求清单与框架均含 notice.broadcast。ctx.notice.broadcast 与 dnf.notice.broadcast 等价,只能在通知回调调用;初始化和规则回调调用都会报错。

字段 类型、默认值与限制
text 必填 string;非空白、最多 511 个 UTF-16 码元;非 BMP 字符一般占两个码元
scope 可选 "instance" / "channel";默认 instance,指当前服务端实例
channel_id channel 范围必填正 integer;instance 范围忽略
color 可选 uint32 ARGB;省略或为 0 时使用 0xFFFFD54F
surface 可选,只支持 "native_chat",也是默认值

未知字段报错,不接受 player_id、title 等自创参数。某些无关联会话的活动事件 channel_id=0,应选择实例公告或跳过,不能直接广播到该频道。

成功返回 true 仅表示加入当前事件批次的暂存列表,参数错误直接抛异常,不返回 false。同一事件批次任一后续回调出错,所有暂存公告都被丢弃。成功批次还要经过限流、动作队列、版本检查和角色 mailbox,不能把 true 当作客户端送达确认。

接下来写一个完整插件

以上代码片段帮助理解函数,实际安装还需要完整清单、框架授权和实例配置。可从快速上手完成第一条欢迎公告,再在完整示例中选择业务 Hook 模板。

DNF90 · 服务端 Lua 插件核对版本 cda08ef · 2026-10-26

输入关键词,查找指南和全部事件。

点击结果跳转 · Esc 关闭