服务端(Go): - GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面 - GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新 - 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新) - 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍 - 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0) - 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算 启动器(Python/JS): - 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭 - GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐) - 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
129 lines
3.7 KiB
Go
129 lines
3.7 KiB
Go
// pvfcollectboxbase 把 PVF **共享字符串池**里收集箱的 [base expiration date] 改成未来日期。
|
||
//
|
||
// 原因见 internal/modules/dnf/collectbox/owner.go 的 SnapshotBonus:
|
||
//
|
||
// expiration := state.Expiration[boxIndex] // Insert(放卡片) 不写它;只有 Extend(延期券) 写
|
||
// if expiration.IsZero() {
|
||
// expiration, _ = parseExpiration(definition.BaseExpirationDate) // ← 回退到 base
|
||
// }
|
||
// if !expiration.IsZero() && !now.Before(expiration) { return Bonus{} } // 过期 → 属性不生效
|
||
//
|
||
// 而 localization/collectbox/collectboxinfo.etc 里各箱 base 日期均已是过去:
|
||
//
|
||
// Index1 SAO 2014-12-30 Index2 生肖之灵 2015-09-01
|
||
// Index3 三国志 2017-01-05 Index4 猎龙者 2017-12-31
|
||
// Index5 兽人守护神 2018-12-31
|
||
//
|
||
// 用 RepackStringValues 做**等宽**替换:所有日期都是 19 字符,替换后长度不变,
|
||
// 池内偏移与所有引用保持有效。
|
||
package main
|
||
|
||
import (
|
||
"flag"
|
||
"fmt"
|
||
"os"
|
||
"path/filepath"
|
||
|
||
platformpvf "longheng.io/server/internal/platform/pvf"
|
||
)
|
||
|
||
var replacements = []platformpvf.StringValueReplacement{
|
||
{Old: "2014-12-30 06:00:00", New: "2099-12-30 06:00:00"},
|
||
{Old: "2015-09-01 06:00:00", New: "2099-09-01 06:00:00"},
|
||
{Old: "2017-01-05 06:00:00", New: "2099-01-05 06:00:00"},
|
||
{Old: "2017-12-31 06:00:00", New: "2099-12-31 06:00:00"},
|
||
{Old: "2018-12-31 06:00:00", New: "2099-12-31 06:00:00"},
|
||
}
|
||
|
||
func main() {
|
||
input := flag.String("pvf", "", "input PVF (in place)")
|
||
flag.Parse()
|
||
if *input == "" {
|
||
panic("-pvf is required")
|
||
}
|
||
archive, err := platformpvf.Open(*input)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
// 先确认这些日期确实在池里
|
||
offsets := archive.FindStringMagicOffsets(func() []string {
|
||
out := make([]string, 0, len(replacements))
|
||
for _, r := range replacements {
|
||
out = append(out, r.Old)
|
||
}
|
||
return out
|
||
}())
|
||
fmt.Println("池中命中情况:")
|
||
ok := make([]platformpvf.StringValueReplacement, 0, len(replacements))
|
||
for _, r := range replacements {
|
||
if off, found := offsets[r.Old]; found {
|
||
fmt.Printf(" ✓ %s (池@%d) → %s\n", r.Old, off, r.New)
|
||
ok = append(ok, r)
|
||
} else {
|
||
fmt.Printf(" ✗ %s 不在池中\n", r.Old)
|
||
}
|
||
}
|
||
if len(ok) == 0 {
|
||
panic("没有任何可替换的日期")
|
||
}
|
||
repacked, err := archive.RepackStringValues(ok)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
fmt.Printf("重打包完成:%d → %d 字节\n", len(archive.Bytes()), len(repacked))
|
||
|
||
// 校验:新 PVF 里日期已变
|
||
verify, err := platformpvf.OpenBytes(repacked)
|
||
if err != nil {
|
||
fmt.Println("⚠ 无法重新打开校验:", err)
|
||
} else {
|
||
text, err := verify.ReadText("localization/collectbox/collectboxinfo.etc")
|
||
if err == nil {
|
||
for _, r := range ok {
|
||
if contains(text, r.New) {
|
||
fmt.Printf(" ✓ 已确认含 %s\n", r.New)
|
||
}
|
||
if contains(text, r.Old) {
|
||
fmt.Printf(" ✗ 仍含旧值 %s\n", r.Old)
|
||
}
|
||
}
|
||
} else {
|
||
fmt.Println(" ⚠ 校验读取失败:", err)
|
||
}
|
||
}
|
||
|
||
// 原子写回:先写临时文件再替换
|
||
dir := filepath.Dir(*input)
|
||
tmp := filepath.Join(dir, filepath.Base(*input)+".tmp_collectbox")
|
||
if err := os.WriteFile(tmp, repacked, 0o644); err != nil {
|
||
panic(err)
|
||
}
|
||
backup := *input + ".bak_before_basefix"
|
||
if _, statErr := os.Stat(backup); statErr != nil {
|
||
if err := os.Rename(*input, backup); err != nil {
|
||
panic(err)
|
||
}
|
||
fmt.Println("已备份 →", backup)
|
||
} else {
|
||
if err := os.Remove(*input); err != nil {
|
||
panic(err)
|
||
}
|
||
}
|
||
if err := os.Rename(tmp, *input); err != nil {
|
||
panic(err)
|
||
}
|
||
fmt.Println("已写回", *input)
|
||
}
|
||
|
||
func contains(haystack, needle string) bool {
|
||
if len(needle) == 0 || len(haystack) < len(needle) {
|
||
return false
|
||
}
|
||
for i := 0; i+len(needle) <= len(haystack); i++ {
|
||
if haystack[i:i+len(needle)] == needle {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|