接口总览
本页列出当前 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
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 顺序。添加、删去或禁用任一插件,下一次成功重载都会重建整个集合。
配置集合应写成字符串键映射:
-- 正确:可用于按角色 ID 查表。
config = { blocked_ids = { ["123"] = true, ["456"] = true } }
-- 错误:config 内不接受数字键数组。
-- config = { blocked_ids = { "123", "456" } }
dnf.plugins.clear_announcement
dnf.plugins.clear_announcement("auto")
| 模式 | 行为 |
|---|---|
auto |
默认。存在唯一 dungeon.clear_announcement provider 时由插件负责,否则用内置播报 |
builtin |
用原内置播报,该点的 before/provider/transform 均不执行;普通 dungeon.cleared 订阅仍执行 |
plugin |
必须存在唯一播报 provider,否则整个候选集合被拒绝;插件播报失败不补发内置公告 |
即使选择 builtin,同一个扩展点存在多个 provider 仍会导致候选校验失败。
清单:plugin.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 是同一张表。未提供配置时得到空表。
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
dnf.events.on("character.bound", function(ctx, event)
local id = event.data.id
local channel_id = event.data.channel_id
end)
仅初始化时注册,回调返回值被忽略。每插件最多 64 个通知回调。通知统一接收信封:
-- 结构示意,值为示例。
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
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
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
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 模板。