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,119 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"unicode/utf16"
|
||||
|
||||
"pvfine/internal/pvf"
|
||||
)
|
||||
|
||||
const vendingTestText = "# 😀 [/item group]\r\n[name]\r\n`抽奖机 [/output]`\r\n[item group]\r\n[group num]\r\n1\r\n[material]\r\n2 1\r\n[output]\r\n1 3000 2 0\r\n1 20 4 1\r\n[/output]\r\n[unknown]\r\n`保留`\r\n[/item group]\r\n"
|
||||
|
||||
func TestVendingMachineGroupsAndSource(t *testing.T) {
|
||||
text := vendingTestText + "[item group][group num]2[material]3 2[output][/output][/item group]"
|
||||
doc, err := parseVendingMachine(text)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(doc.Groups) != 2 || len(doc.Groups[0].Rewards) != 2 || len(doc.Groups[1].Rewards) != 0 {
|
||||
t.Fatalf("groups: %+v", doc.Groups)
|
||||
}
|
||||
units := utf16.Encode([]rune(strings.ReplaceAll(text, "\r\n", "\n")))
|
||||
g := doc.Groups[0]
|
||||
if string(utf16.Decode(units[g.Start:g.End])) != "[item group]\n[group num]\n1\n[material]\n2 1\n[output]\n1 3000 2 0\n1 20 4 1\n[/output]\n[unknown]\n`保留`\n[/item group]" {
|
||||
t.Fatal("incorrect group span")
|
||||
}
|
||||
if string(utf16.Decode(units[g.OutputClose:g.OutputClose+9])) != "[/output]" {
|
||||
t.Fatal("incorrect insertion offset")
|
||||
}
|
||||
for _, row := range g.Rewards {
|
||||
for _, f := range row.Fields {
|
||||
if string(utf16.Decode(units[f.Start:f.End])) == "" {
|
||||
t.Fatal("empty numeric span")
|
||||
}
|
||||
}
|
||||
}
|
||||
if g.Rewards[1].Fields[2].Value != 4 || g.Rewards[1].Fields[3].Value != 1 {
|
||||
t.Fatal("reward columns")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVendingMachineRejectsInvalidStructure(t *testing.T) {
|
||||
good := "[item group][group num]1[material]2 1[output]1 10 1 0[/output][/item group]"
|
||||
cases := []string{
|
||||
strings.ReplaceAll(good, "[/item group]", ""),
|
||||
strings.ReplaceAll(good, "[/output]", ""),
|
||||
strings.ReplaceAll(good, "1 10 1 0", "1 10 1"),
|
||||
strings.ReplaceAll(good, "1 10 1 0", "1 10 1 2"),
|
||||
strings.ReplaceAll(good, "1 10 1 0", "1 -1 1 0"),
|
||||
strings.ReplaceAll(good, "1 10 1 0", "1 2147483648 1 0"),
|
||||
strings.ReplaceAll(good, "1 10 1 0", "1 1.0 1 0"),
|
||||
strings.ReplaceAll(good, "[material]2 1", "[material]2 1[material]2 1"),
|
||||
strings.ReplaceAll(good, "[material]2 1", "[material]2"),
|
||||
strings.ReplaceAll(good, "[group num]1", ""),
|
||||
good + good,
|
||||
"[material]2 1",
|
||||
"[item group]" + good + "[/item group]",
|
||||
}
|
||||
for _, text := range cases {
|
||||
if _, err := parseVendingMachine(text); err == nil {
|
||||
t.Errorf("accepted invalid: %s", text)
|
||||
}
|
||||
}
|
||||
if doc, err := parseVendingMachine("# empty\n[name]`新抽奖机`"); err != nil || len(doc.Groups) != 0 {
|
||||
t.Fatalf("empty: %+v %v", doc, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVendingMachineListAndRead(t *testing.T) {
|
||||
c, _ := shopFixture(t)
|
||||
a := c.archive
|
||||
i, err := a.AddFileText("etc/vending/a.vm", vendingTestText, pvf.TypeScript)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err = a.AddFileText("etc/vendingmachine.lst", "10 `vending/a.vm` 11 `etc/vending/a.vm` 12 `missing.vm` 13 `../stackable/one.stk`", pvf.TypeScript); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
service := NewFileGUIService(c)
|
||||
before := a.ModifiedCount()
|
||||
entries, err := service.ListVendingMachines()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(entries) != 2 || entries[0].ID != "10" || entries[1].ID != "11" || entries[0].FileIndex != i {
|
||||
t.Fatalf("entries: %+v", entries)
|
||||
}
|
||||
doc, err := service.ReadVendingMachine(i, vendingTestText)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if doc.Groups[0].MaterialName != "材料甲" || doc.Groups[0].Rewards[0].Name != "长名称药剂" {
|
||||
t.Fatalf("names: %+v", doc.Groups[0])
|
||||
}
|
||||
if a.ModifiedCount() != before {
|
||||
t.Fatal("read mutated archive")
|
||||
}
|
||||
other, _ := a.Find("stackable/one.stk")
|
||||
if _, err := service.ReadVendingMachine(other, vendingTestText); err == nil {
|
||||
t.Fatal("accepted non vm")
|
||||
}
|
||||
if _, err := service.ReadVendingMachine(-1, vendingTestText); err == nil {
|
||||
t.Fatal("accepted bad index")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVendingMachineNoArchiveOrList(t *testing.T) {
|
||||
service := NewFileGUIService(NewCore())
|
||||
if _, err := service.ListVendingMachines(); err == nil {
|
||||
t.Fatal("accepted no archive")
|
||||
}
|
||||
if _, err := service.ReadVendingMachine(0, ""); err == nil {
|
||||
t.Fatal("accepted no archive")
|
||||
}
|
||||
service.c.archive = pvf.New()
|
||||
if _, err := service.ListVendingMachines(); err == nil {
|
||||
t.Fatal("accepted missing list")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user