Files
hanfuit 645b64fe21 DNF90 服务端与启动器完整源码(含 GM 工具功能增强)
服务端(Go):
- GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面
- GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新
- 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新)
- 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍
- 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0)
- 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算

启动器(Python/JS):
- 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭
- GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐)
- 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
2026-11-04 14:31:55 +08:00

170 lines
4.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// pvfrawdisableattendance 从 Script.pvf 读出「累计在线得好礼」活动定义文件的
// **原始字节**,把奖励与阶段数据等长清空,导出到 pvfrepack 所需的 raw 目录结构,
// 之后由 pvfrepack 重新封包。
//
// 为什么必须走 raw:
//
// pvfrepack 明确说明"decoded 可读文本不能直接重新封包"——直接用 ReadText 得到的
// 文本重新写回会破坏 type-1 成员的令牌流(实测:重打包后该文件只剩 1 行)。
// 所以这里只改 raw 字节,且**保持文件长度完全不变**。
//
// 目标文件:event/chn_event/chn_attendanceevent.evt
//
// go run ./cmd/scratch/pvfrawdisableattendance -pvf <in.pvf> -dir <raw目录>
package main
import (
"flag"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
platformpvf "longheng.io/server/internal/platform/pvf"
)
const targetPath = "event/chn_event/chn_attendanceevent.evt"
func main() {
pvfPath := flag.String("pvf", "", "源 Script.pvf")
dir := flag.String("dir", "", "raw 修改目录(按 PVF 相对路径存放)")
flag.Parse()
if *pvfPath == "" || *dir == "" {
fmt.Fprintln(os.Stderr, "usage: pvfrawdisableattendance -pvf <in.pvf> -dir <rawdir>")
os.Exit(2)
}
archive, err := platformpvf.Open(*pvfPath)
if err != nil {
fmt.Fprintln(os.Stderr, "open:", err)
os.Exit(1)
}
raw, err := archive.ReadRaw(targetPath)
if err != nil {
fmt.Fprintln(os.Stderr, "read raw:", err)
os.Exit(1)
}
fmt.Printf("原始字节 %d\n", len(raw))
patched := patchRaw(raw)
if len(patched) != len(raw) {
fmt.Fprintf(os.Stderr, "!! 长度变化 %d -> %d,中止\n", len(raw), len(patched))
os.Exit(1)
}
fmt.Printf("改后字节 %d(等长)\n", len(patched))
out := filepath.Join(*dir, filepath.FromSlash(targetPath))
if err := os.MkdirAll(filepath.Dir(out), 0o755); err != nil {
fmt.Fprintln(os.Stderr, "mkdir:", err)
os.Exit(1)
}
if err := os.WriteFile(out, patched, 0o644); err != nil {
fmt.Fprintln(os.Stderr, "write:", err)
os.Exit(1)
}
fmt.Printf("已写出 raw: %s\n", out)
}
// patchRaw 在原始字节上做等长替换。
//
// type-1 成员里,段标签与数值都是"反引号包裹的 ASCII 字面量"(例如 `1800`),
// 因此只需在 ASCII 范围内把数字位换成 9、把奖励物品号整段换成空格,长度不变。
func patchRaw(raw []byte) []byte {
out := make([]byte, len(raw))
copy(out, raw)
// 1) [reward item] / [reward item for sum] 段内的物品号清空。
out = blankRawSection(out, "reward item")
out = blankRawSection(out, "reward item for sum")
// 2) [process time seconds] / [process seconds for max count] 的数值改为极大,
// 使阶段永远不可达(等长:数字位数不变)。
out = maxRawSectionNumbers(out, "process time seconds")
out = maxRawSectionNumbers(out, "process seconds for max count")
return out
}
// findRawTag 在原始字节里找到以 `[tag]` 形式出现的偏移。
func findRawTag(data []byte, tag string) int {
needle := []byte("[`" + tag + "`]")
if i := strings.Index(string(data), string(needle)); i >= 0 {
return i
}
// 兼容无内层反引号的写法
needle2 := []byte("[" + tag + "]")
return strings.Index(string(data), string(needle2))
}
// blankRawSection 把 `[tag] ... [/tag]` 之间的可打印字符(非结构性 '`')换成空格。
func blankRawSection(data []byte, tag string) []byte {
start := findRawTag(data, tag)
if start < 0 {
fmt.Printf(" 提示:未找到段 [%s]\n", tag)
return data
}
openEnd := start
for openEnd < len(data) && data[openEnd] != ']' {
openEnd++
}
openEnd++ // 越过 ']'
closeTag := "[/`" + tag + "`]"
end := strings.Index(string(data[openEnd:]), closeTag)
if end < 0 {
closeTag = "[/" + tag + "]"
end = strings.Index(string(data[openEnd:]), closeTag)
}
if end < 0 {
fmt.Printf(" 提示:[%s] 没有闭合标签\n", tag)
return data
}
bodyEnd := openEnd + end
for i := openEnd; i < bodyEnd; i++ {
c := data[i]
// 只改数字,避免碰结构字符(反引号、方括号、换行)
if c >= '0' && c <= '9' {
data[i] = ' '
}
}
return data
}
// maxRawSectionNumbers 把段内数字改为 9(等长),使门槛极大。
func maxRawSectionNumbers(data []byte, tag string) []byte {
start := findRawTag(data, tag)
if start < 0 {
fmt.Printf(" 提示:未找到段 [%s]\n", tag)
return data
}
openEnd := start
for openEnd < len(data) && data[openEnd] != ']' {
openEnd++
}
openEnd++
closeTag := "[/`" + tag + "`]"
end := strings.Index(string(data[openEnd:]), closeTag)
if end < 0 {
closeTag = "[/" + tag + "]"
end = strings.Index(string(data[openEnd:]), closeTag)
}
if end < 0 {
return data
}
bodyEnd := openEnd + end
re := regexp.MustCompile(`[0-9]+`)
seg := data[openEnd:bodyEnd]
for _, loc := range re.FindAllIndex(seg, -1) {
for i := loc[0]; i < loc[1]; i++ {
seg[i] = '9'
}
}
return data
}