DPForge Next
DNF 私服插件管理 · 模块配置 · 一键生成部署 · GM 工具集。在浏览器里勾选功能、编辑参数、生成 df_game_r.js / Lua / config,并通过 SSH 一键部署到服务器。
核心能力
插件配置
按分类勾选功能、编辑参数、查看 Hook 地址;改动即入库,重新生成即写入 df_game_r.js。
Lua 脚本
Lua 道具处理与启动开关,配合 Work_Reload.lua 部署。
生成部署
一键生成 df_game_r.js / Lua / config,SSH 上传到服务器,目标目录自动创建。
GM 管理
角色 / 账号 / 邮件 / 背包 / 道具搜索 / 在线玩家,直接读写游戏库。
服务器管理
SSH 部署、PVF 上传校验、Web Shell、远程文件在线编辑。
系统设置
改口令、初始化代码、frida 配置,兼容 MySQL 5.1–5.7 老库。
为什么要有在线插件市场?
免更新拿新插件
插件定义与注入代码托管在云端,DPForge 直接拉取最新版,不必重装程序。
社区共建
任何人都能在「提交插件」页在线投稿,站长审核通过后即刻对所有人可用。
标准化格式
每个插件 = 一个 JSON(元信息 + Hook + 设置项 + 注入代码),可直接导入工具。
常用操作
插件市场
从云端加载最新插件,点卡片查看设置项与注入代码,一键复制 JSON 导入 DPForge。想贡献自己的插件?看 开发文档 › 贡献流程。
提交插件
在线填写并提交,进入待审核队列;站长审核通过后即刻上架,所有 DPForge 用户可拉取。无需 Git、无需私有部署。提交前请阅读 开发文档 与 安全红线。
插件是什么
DPForge 的每个「插件」对应 DNF 服务端进程里的一段 Frida 注入逻辑。工具会把所有已启用插件的代码拼进 df_game_r.js,随服务端加载执行,从而实现登入播报、副本玩法、装备道具、运营管控等功能。
一个插件在云端就是一个 JSON 文件:元信息 + Hook 地址 + 设置项 schema + 注入代码。DPForge 拉取后即可在「插件配置」页勾选、改参数、生成部署。
插件 JSON 结构
把下面的对象保存成 plugins/<id>.json,并在 plugins/index.json 里登记一行即可:
{
"schema": 1,
"id": "extra_monster_reward", // 唯一 id(英文/下划线)
"cat": "dungeon", // 分类 id(见 index.json.categories)
"name": "副本随机稀有怪", // 显示名
"ico": "👹", // emoji 图标
"version": "1.0.0", // 语义化版本
"author": "your-name", // 贡献者
"initFn": "hook_extra_monster", // 入口函数名(会被调用)
"desc": "进房间按概率追加稀有怪并掉落道具。",
"tags": ["副本玩法"],
"hooks": [ // 仅作展示/说明,实际 attach 在 code 内
{ "addr": "0x1234ABCD", "mode": "attach", "dup": false }
],
"settings": [ /* 见「插件设置配置」 */ ],
"code": "function hook_extra_monster(){ /* Frida 代码 */ }",
"updatedAt": "2026-07-17"
}
id 全局唯一;cat 必须是 index.json 里存在的分类;initFn 必须与 code 中的入口函数同名——DPForge 生成时会调用它。注入处理逻辑
注入代码就是标准的 Frida JavaScript。约定:把所有逻辑包在一个与 initFn 同名的函数里,在函数内部用 Interceptor.attach 挂目标地址。
最小示例(强制某函数返回 1)
// initFn: HookDsSwordman_SkillSlot
function HookDsSwordman_SkillSlot() {
Interceptor.attach(ptr(0x08608C98), { // SkillSlot::checkMoveComboSkillSlot
onEnter: function (args) { /* args[0]=this, args[1..]=参数 */ },
onLeave: function (retval) { retval.replace(1) } // 改返回值
});
}
读取参数 / 调用游戏内函数
function hook_user_inout_game_world() {
Interceptor.attach(ptr(0x86C4E50), { // GameWorld::reach_game_world
onEnter: function (args) { this.user = args[1]; },
onLeave: function () {
var name = api_CUserCharacInfo_getCurCharacName(this.user);
sendWelcomeMessage(this.user, name); // 复用框架已有的公共函数
}
});
}
0x…)与服务端二进制版本强绑定,跨版本需重新定位。贡献插件时请注明适用的服务端版本,并尽量把地址、魔法数写成显式常量。内存补丁(无 Hook)
// initFn: baitan14 —— 放开个人摆摊格子上限到 14
function baitan14() {
Memory.protect(ptr(0xXXXXXXX), 4, 'rwx');
ptr(0xXXXXXXX).writeU8(14);
}
hooks[] 声明(元数据,非代码)
把 code 里每个 Hook 的地址在 hooks 里登记一条,供 DPForge 做冲突检测与展示——它不生成代码。
| 字段 | 说明 |
|---|---|
addr | 目标地址,与 code 里 ptr(0x…) 一致 |
mode | attach=Interceptor.attach(观察/改参/改返回,可共存);replace=Interceptor.replace(替换整函数,独占);patch=Memory 写字节(独占,同址=字节冲突) |
dup | 可选,true=已知与其它模块共用该地址(抑制重复告警) |
patch → 🔴 报错;多个 replace → 🟠 警告;多个 attach → 标 dup(通常可共存)。插件设置配置
把可调参数写进 settings 数组,DPForge 会在「基本设置」里自动渲染成输入框 / 开关,并把用户填的值替换进代码中的 {{key}} 占位符。
"settings": [
{ "key": "boss_start_hour", "label": "开启时间(点)", "type": "number",
"default": 21, "description": "每天几点整开启活动 (0-23)" },
{ "key": "phase2_dungeon_name", "label": "二阶段副本名", "type": "text",
"default": "罗特斯宫殿", "description": "广播文案中显示的副本名称" },
{ "key": "phase1_unique_team", "label": "一阶段队伍去重", "type": "toggle",
"default": false, "description": "同一队伍多次通关只计一次" }
]
| 字段 | 说明 |
|---|---|
key | 参数名,同时是代码里的占位符 {{key}} |
label | 界面显示名 |
type | number/text/toggle/select/itemId/table/json(高级) |
default | 默认值(原来写死的那个值) |
advanced | 可选,true 标为「高级」参数(json 类推荐加) |
description | 参数说明(显示在输入框旁) |
高级参数(数组 / 对象)用 type:"json"
{ "key": "phase1_dungeons", "label": "一阶段地图列表", "type": "json", "advanced": true,
"default": [ { "dgn_id": 161, "name": "鲨鱼栖息地", "team_target": 5 } ],
"description": "[{dgn_id, name, team_target}, ...] 支持任意张图" }
在代码里使用占位符(都裸写)
var START_HOUR = {{boss_start_hour}}; // number → 21
var DUNGEON = {{phase2_dungeon_name}}; // text → "罗特斯宫殿"(自动带引号!)
var DUNGEONS = {{phase1_dungeons}}; // json → [{...}]
if ({{phase1_unique_team}}) { } // toggle → true / false
{{key}} 被替换成该值的 JSON 字面量(json.Marshal)。文本会自动带引号,所以模板里写 name = {{k}},不要写 "{{k}}"(否则变成双引号,语法错误)。number/toggle/json 同理,一律裸写。plugin-dev/插件模块化改造指南.md。在 DPForge 中使用在线插件
DPForge 客户端已内置指向本站,无需任何配置:
- 打开 DPForge → 插件配置 → 点右上角 🌐 在线插件。
- 在列表里选插件,点 导入 / 更新(已安装的会显示「更新」)。
- 导入后到对应分类里勾选启用,再「⚡ 生成部署」即可。
也可在本站插件详情里「📋 复制插件 JSON」手动保存备份。
提交插件(在线投稿)
不需要 Git、不需要自己部署。直接在本站 提交插件 页填写并提交:
- 打开 提交插件,填 id / 分类 / 名称 / initFn / 说明 / 设置项 / 注入代码(可点「填入示例」参考)。
- 点「提交审核」,你的插件进入待审核队列。
- 审核通过后即刻上架,所有 DPForge 用户可在「🌐 在线插件」里拉取。
- 想更新已上架的插件?用同一个 id 再提交一次,审核通过即覆盖为新版。
Interceptor 目标、网络请求与内存写入,可疑代码一律拒绝。