DNF90 服务端与启动器完整源码(含 GM 工具功能增强)
服务端(Go): - GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面 - GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新 - 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新) - 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍 - 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0) - 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算 启动器(Python/JS): - 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭 - GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐) - 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
This commit is contained in:
commit
645b64fe21
4388 files changed
+1405457
No files matched your search
@@ -0,0 +1,160 @@
|
||||
// Scratch tool: inspect the social (friend / guild / party) UI definitions and
|
||||
// any UI text inside the shipped Script.pvf.
|
||||
//
|
||||
// usage:
|
||||
//
|
||||
// go run ./cmd/scratch/pvfsocial <pvf> list <pathSubstring> [max]
|
||||
// go run ./cmd/scratch/pvfsocial <pvf> dump <pathSubstring> [maxFiles]
|
||||
// go run ./cmd/scratch/pvfsocial <pvf> find <text> [pathFilter] [maxHits]
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"longheng.io/server/internal/platform/pvf"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 4 {
|
||||
fmt.Println("usage: pvfsocial <pvf> list|dump|find <arg> [extra] [max]")
|
||||
return
|
||||
}
|
||||
path, mode, needle := os.Args[1], os.Args[2], os.Args[3]
|
||||
extra := ""
|
||||
if len(os.Args) > 4 {
|
||||
extra = os.Args[4]
|
||||
}
|
||||
limit := 0
|
||||
if len(os.Args) > 5 {
|
||||
if n, err := strconv.Atoi(os.Args[5]); err == nil && n > 0 {
|
||||
limit = n
|
||||
}
|
||||
}
|
||||
|
||||
bundle, err := pvf.Load(pvf.Options{Path: path, MaxBytes: pvf.NoMaxBytes})
|
||||
if err != nil {
|
||||
fmt.Println("load:", err)
|
||||
return
|
||||
}
|
||||
archive, err := pvf.OpenArchive(bundle)
|
||||
if err != nil {
|
||||
fmt.Println("open:", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("archive format=%s files=%d readable=%v\n", archive.Format(), archive.FileCount(), archive.CanReadFileData())
|
||||
|
||||
switch mode {
|
||||
case "list":
|
||||
printList(archive, needle, limit)
|
||||
case "dump":
|
||||
dump(archive, strings.ToLower(needle), limit)
|
||||
case "find":
|
||||
search(archive, needle, strings.ToLower(extra), limit)
|
||||
default:
|
||||
fmt.Println("unknown mode", mode)
|
||||
}
|
||||
}
|
||||
|
||||
func printList(archive *pvf.Archive, needle string, limit int) {
|
||||
if limit == 0 {
|
||||
limit = 400
|
||||
}
|
||||
low := strings.ToLower(strings.ReplaceAll(needle, "\\", "/"))
|
||||
count := 0
|
||||
archive.RangePaths(func(p string) bool {
|
||||
if strings.Contains(strings.ToLower(strings.ReplaceAll(p, "\\", "/")), low) {
|
||||
count++
|
||||
if count <= limit {
|
||||
fmt.Println(" " + p)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
fmt.Printf("paths containing %q: %d (shown %d)\n", needle, count, min(count, limit))
|
||||
}
|
||||
|
||||
func dump(archive *pvf.Archive, low string, maxFiles int) {
|
||||
if maxFiles == 0 {
|
||||
maxFiles = 4
|
||||
}
|
||||
shown := 0
|
||||
var matches []string
|
||||
archive.RangePaths(func(p string) bool {
|
||||
if strings.Contains(strings.ToLower(strings.ReplaceAll(p, "\\", "/")), low) {
|
||||
matches = append(matches, p)
|
||||
}
|
||||
return true
|
||||
})
|
||||
for _, m := range matches {
|
||||
if shown >= maxFiles {
|
||||
break
|
||||
}
|
||||
text, err := archive.ReadText(m)
|
||||
if err != nil {
|
||||
fmt.Printf("=== %s: read error: %v\n", m, err)
|
||||
continue
|
||||
}
|
||||
shown++
|
||||
fmt.Printf("\n================ %s (%d chars) ================\n", m, len(text))
|
||||
fmt.Println(text)
|
||||
}
|
||||
if shown == 0 {
|
||||
fmt.Println("no path matched")
|
||||
}
|
||||
}
|
||||
|
||||
// search reads the text of every file whose path contains pathFilter and
|
||||
// reports the ones whose body contains needle.
|
||||
func search(archive *pvf.Archive, needle, pathFilter string, maxHits int) {
|
||||
if pathFilter == "" {
|
||||
pathFilter = "ui/"
|
||||
}
|
||||
if maxHits == 0 {
|
||||
maxHits = 40
|
||||
}
|
||||
scanned, hits := 0, 0
|
||||
var paths []string
|
||||
archive.RangePaths(func(p string) bool {
|
||||
if strings.Contains(strings.ToLower(strings.ReplaceAll(p, "\\", "/")), pathFilter) {
|
||||
paths = append(paths, p)
|
||||
}
|
||||
return true
|
||||
})
|
||||
fmt.Printf("scanning %d files under %q for %q\n", len(paths), pathFilter, needle)
|
||||
for _, p := range paths {
|
||||
text, err := archive.ReadText(p)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
scanned++
|
||||
if !strings.Contains(text, needle) {
|
||||
continue
|
||||
}
|
||||
hits++
|
||||
fmt.Printf("HIT %s\n", p)
|
||||
for _, line := range strings.Split(text, "\n") {
|
||||
if strings.Contains(line, needle) {
|
||||
trimmed := strings.TrimSpace(line)
|
||||
if len(trimmed) > 260 {
|
||||
trimmed = trimmed[:260] + "..."
|
||||
}
|
||||
fmt.Printf(" %s\n", trimmed)
|
||||
}
|
||||
}
|
||||
if hits >= maxHits {
|
||||
fmt.Println(" (hit limit reached)")
|
||||
break
|
||||
}
|
||||
}
|
||||
fmt.Printf("scanned=%d hits=%d\n", scanned, hits)
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
Reference in new issue
Block a user