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

164 lines
4.4 KiB
Go

package achievement
import (
"context"
"testing"
dnfrepo "longheng.io/server/internal/modules/dnf/repository"
dnfrepomemory "longheng.io/server/internal/modules/dnf/repository/memory"
)
type definitionStub Definition
func (r definitionStub) ResolveAchievementDefinition(context.Context, int32) (Definition, error) {
return Definition(r), nil
}
func TestOwnerTriggerPersistsRealTargetAndGrantsTitleOnce(t *testing.T) {
ctx := context.Background()
repos := dnfrepomemory.NewMemoryGroup()
if err := repos.Inventory.Save(ctx, dnfrepo.InventoryRecord{
CharacterID: "19",
Slots: map[string]dnfrepo.ItemStack{},
}); err != nil {
t.Fatalf("save inventory: %v", err)
}
owner, err := NewOwner(repos, definitionStub{
QuestID: 6533,
Target1: 4,
Reward: TitleReward{
ItemID: 9001,
Category: 2,
BookIndex: 7,
},
})
if err != nil {
t.Fatalf("NewOwner: %v", err)
}
first, err := owner.Trigger(ctx, Command{
SelectedCharacterID: 19,
QuestID: 6533,
Delta1: 1,
})
if err != nil {
t.Fatalf("first Trigger: %v", err)
}
if first.Remain1 != 3 || first.Completed {
t.Fatalf("first result = %+v", first)
}
second, err := owner.Trigger(ctx, Command{
SelectedCharacterID: 19,
QuestID: 6533,
Delta1: 3,
})
if err != nil {
t.Fatalf("second Trigger: %v", err)
}
if !second.Completed || !second.TitleGranted || second.TitleSlot != 2007 {
t.Fatalf("second result = %+v", second)
}
third, err := owner.Trigger(ctx, Command{
SelectedCharacterID: 19,
QuestID: 6533,
Delta1: 1,
})
if err != nil {
t.Fatalf("third Trigger: %v", err)
}
if third.Completed || third.TitleGranted || !third.AlreadyCompleted ||
third.Remain1 != 0 || third.Remain2 != 0 || third.Remain3 != 0 {
t.Fatalf("third result = %+v", third)
}
inventory, found, err := repos.Inventory.Load(ctx, "19")
if err != nil || !found {
t.Fatalf("load inventory found=%t err=%v", found, err)
}
if title := inventory.Slots["100:2007"]; title.ItemID != 9001 || title.Count != 1 {
t.Fatalf("title = %+v", title)
}
for key, stack := range inventory.Slots {
if IsLegacyInventoryProgress(key, stack) {
t.Fatalf("achievement progress leaked into title inventory: %s=%+v", key, stack)
}
}
quests, found, err := repos.Quest.Load(ctx, "19")
if err != nil || !found {
t.Fatalf("load quests found=%t err=%v", found, err)
}
progress := Snapshot(quests)
if len(progress) != 1 || progress[0].QuestID != 6533 || !progress[0].Completed {
t.Fatalf("progress = %+v", progress)
}
}
func TestOwnerTriggerFallsBackToFirstFreeTitleSlot(t *testing.T) {
ctx := context.Background()
repos := dnfrepomemory.NewMemoryGroup()
if err := repos.Inventory.Save(ctx, dnfrepo.InventoryRecord{
CharacterID: "19",
Slots: map[string]dnfrepo.ItemStack{
"100:2007": {ItemID: 8001, Count: 1},
"100:2000": {ItemID: 8002, Count: 1},
},
}); err != nil {
t.Fatalf("save inventory: %v", err)
}
owner, err := NewOwner(repos, definitionStub{
QuestID: 6533,
Target1: 1,
Reward: TitleReward{ItemID: 9001, Category: 2, BookIndex: 7},
})
if err != nil {
t.Fatalf("NewOwner: %v", err)
}
result, err := owner.Trigger(ctx, Command{SelectedCharacterID: 19, QuestID: 6533, Delta1: 1})
if err != nil {
t.Fatalf("Trigger: %v", err)
}
if result.TitleSlot != 2001 {
t.Fatalf("title slot = %d, want 2001", result.TitleSlot)
}
}
func TestRepairLegacyRowsRemovesOnlyBrokenProgressSignature(t *testing.T) {
ctx := context.Background()
repos := dnfrepomemory.NewMemoryGroup()
if err := repos.Inventory.Save(ctx, dnfrepo.InventoryRecord{
CharacterID: "19",
Slots: map[string]dnfrepo.ItemStack{
"100:933": {
ItemID: 6533,
Count: 1,
Extra: map[string]string{
"initialized": "1",
"quest_id": "6533",
"remain1": "1",
},
},
"100:7": {ItemID: 9001, Count: 1},
},
}); err != nil {
t.Fatalf("save inventory: %v", err)
}
owner, err := NewOwner(repos, nil)
if err != nil {
t.Fatalf("NewOwner: %v", err)
}
removed, err := owner.RepairLegacyRows(ctx, 19)
if err != nil {
t.Fatalf("RepairLegacyRows: %v", err)
}
if removed != 1 {
t.Fatalf("removed = %d, want 1", removed)
}
inventory, _, _ := repos.Inventory.Load(ctx, "19")
if _, found := inventory.Slots["100:933"]; found {
t.Fatalf("legacy progress survived: %+v", inventory.Slots["100:933"])
}
if inventory.Slots["100:7"].ItemID != 9001 {
t.Fatalf("real title was removed: %+v", inventory.Slots)
}
}