Files
90CNServer/pvf/internal/script/upgrade_test.go
T
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

145 lines
5.3 KiB
Go

package script
import (
"context"
"strconv"
"strings"
"testing"
"pvfine/internal/pvf"
)
const scriptUpgradeRow = "2.0 0 0 0 0 0.6 0 0.828 90000 88000 2 3 3 0 3037 10 0.3 0.018 0.1 0.033 0.17 0.228 0 0.2622"
func TestScriptUpgradeFunctionsAndAtomicValidation(t *testing.T) {
for _, test := range []struct {
name, source string
failed bool
}{
{"range", `const f=pvf.find("etc/upgrade.etc"); if(f.editUpgrade({equipment:"armor",fromLevel:2,toLevel:3,successRate:level=>100-level*5,costCount:(level,current)=>current+level*2})!==2)throw Error("count");`, false},
{"invalid late callback", `const f=pvf.find("etc/upgrade.etc");const before=f.text();let rejected=false;try{f.editUpgrade({successRate:level=>level===3?101:50,costCount:level=>level*20});}catch(e){rejected=true;}if(!rejected||f.text()!==before)throw Error("atomic validation failed");`, false},
{"fraction count", `pvf.find("etc/upgrade.etc").editUpgrade({costCount:level=>level/2});`, true},
{"string result", `pvf.find("etc/upgrade.etc").editUpgrade({successRate:()=>"50"});`, true},
{"bad range", `pvf.find("etc/upgrade.etc").editUpgrade({fromLevel:0,successRate:50});`, true},
} {
t.Run(test.name, func(t *testing.T) {
a := pvf.New()
defer a.Release()
text := "[name]\n`😀`\n[table]\n[table type]\n`event` 1 2 3\n[/table]\n[table]\n[table type]\n`normal` " + strings.Repeat(scriptUpgradeRow+"\n", 3) + "[/table]\n[table]\n[table type]\n`amplify` " + scriptUpgradeRow + "\n[/table]"
index, err := a.AddFileText("etc/upgrade.etc", text, pvf.TypeScript)
if err != nil {
t.Fatal(err)
}
tx := NewTransaction(a)
defer tx.Rollback()
host := NewBatchAPI(context.Background(), tx, nil, nil)
before, err := tx.Stage().Text(index)
if err != nil {
t.Fatal(err)
}
result, err := NewGojaRuntime().Run(context.Background(), test.source, host)
if err != nil {
t.Fatal(err)
}
if test.failed {
if result.Status != RunStatusFailed {
t.Fatalf("expected failure: %+v", result)
}
} else if result.Status != RunStatusCompleted {
t.Fatalf("run: %+v", result)
}
after, _ := tx.Stage().Text(index)
if test.name != "range" {
if after != before {
t.Fatal("invalid batch changed file")
}
return
}
tokens, _, err := pvf.SelectUpgradeTableTokens(after, "normal")
if err != nil {
t.Fatal(err)
}
for level := 1; level <= 3; level++ {
row := tokens[(level-1)*24 : level*24]
if row[8].Value != "90000" {
t.Fatal("weapon modified")
}
wantRate, wantCount := 88000, 10
if level >= 2 {
wantRate, wantCount = level*5000, 10+level*2
}
if row[9].Value != strconv.Itoa(wantRate) || row[15].Value != strconv.Itoa(wantCount) {
t.Fatalf("level %d: %+v", level, row)
}
}
original, _ := a.Text(index)
if original != before {
t.Fatal("script changed base archive")
}
unchanged, _, _ := pvf.SelectUpgradeTableTokens(after, "amplify")
if unchanged[9].Value != "88000" || unchanged[15].Value != "10" {
t.Fatal("wrong table changed")
}
})
}
}
func TestUpgradeSourceOffsets(t *testing.T) {
text := "`😀`\r\n123\r456"
offsets := upgradeByteOffsets(text)
for _, e := range pvf.ParseScriptView(text).Elements {
if e.TokenType == 0 {
if text[offsets[e.Start]:offsets[e.End]] != e.Value {
t.Fatal("invalid CRLF/UTF-16 positions")
}
}
}
}
const scriptUpgrade19Row = "2.2 0.018 0.1 0.033 0.2 0.6 0 10000 2 1 3037 10 1 0.018 0.1 0.033 0.17 0.228 0"
func TestScriptUpgrade19Columns(t *testing.T) {
a := pvf.New()
defer a.Release()
text := "[table]\n[table type]\n`normal`\n" + strings.Repeat(scriptUpgrade19Row+"\n", 5) + "[/table]"
index, err := a.AddFileText("etc/upgrade.etc", text, pvf.TypeScript)
if err != nil {
t.Fatal(err)
}
tx := NewTransaction(a)
defer tx.Rollback()
host := NewBatchAPI(context.Background(), tx, nil, nil)
// equipment: armor 应报错
res, err := NewGojaRuntime().Run(context.Background(), `pvf.find("etc/upgrade.etc").editUpgrade({equipment:"armor",successRate:100});`, host)
if err != nil || res.Status != RunStatusFailed {
t.Fatalf("expected armor edit to fail on 19 column table: %+v", res)
}
// 成功率与数量修改
res, err = NewGojaRuntime().Run(context.Background(), `pvf.find("etc/upgrade.etc").editUpgrade({fromLevel:1,toLevel:2,successRate:100,costCount:88});`, host)
if err != nil || res.Status != RunStatusCompleted {
t.Fatalf("run failed: %+v %v", res, err)
}
after, err := tx.Stage().Text(index)
if err != nil {
t.Fatal(err)
}
tokens, _, err := pvf.SelectUpgradeTableTokens(after, "normal")
if err != nil {
t.Fatal(err)
}
// Level 1: 失败概率在 offset+7 应为 0 (100% 成功率), 数量在 offset+11 应为 88
if tokens[7].Value != "0" || tokens[11].Value != "88" {
t.Fatalf("level 1 not updated correctly: tokens[7]=%s, tokens[11]=%s", tokens[7].Value, tokens[11].Value)
}
// Level 2: 同样被更新
if tokens[19+7].Value != "0" || tokens[19+11].Value != "88" {
t.Fatalf("level 2 not updated correctly: tokens[26]=%s, tokens[30]=%s", tokens[19+7].Value, tokens[19+11].Value)
}
// Level 3: 保持未变 (原本失败率 10000, 数量 10)
if tokens[38+7].Value != "10000" || tokens[38+11].Value != "10" {
t.Fatalf("level 3 should be unchanged: tokens[45]=%s, tokens[49]=%s", tokens[38+7].Value, tokens[38+11].Value)
}
}