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,111 @@
|
||||
package dnfbridge
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
dnfrepo "longheng.io/server/internal/modules/dnf/repository"
|
||||
dnfweaknesspotion "longheng.io/server/internal/modules/dnf/weaknesspotion"
|
||||
)
|
||||
|
||||
// 防虚弱药剂的效果是客户端本地的:活体抓包(2026-09-15 00:43)显示使用只以普通
|
||||
// class-1/op44 上行到达,之后没有任何包携带该状态。所以服务端按道具家族记下持久
|
||||
// 免疫窗口,放弃副本时据此豁免 —— 这条测试钉住"窗口内不惩罚、窗口外照常惩罚"。
|
||||
func TestForcedReturnIsExemptedByAntiWeaknessPotionWindow(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
service, session, repositories, _ := prepareCurrentWeaknessTest(t, 40, 0, 0, nil)
|
||||
characterID := "5"
|
||||
|
||||
arm := func(offset time.Duration) {
|
||||
t.Helper()
|
||||
character, found, err := repositories.Character.Load(ctx, characterID)
|
||||
if err != nil || !found {
|
||||
t.Fatalf("load character err=%v found=%t", err, found)
|
||||
}
|
||||
character = dnfrepo.CloneCharacter(character)
|
||||
if character.Stats == nil {
|
||||
character.Stats = make(map[string]int64, 2)
|
||||
}
|
||||
character.Stats[dnfweaknesspotion.ImmuneUntilStatKey] = service.gameplayNow().Add(offset).Unix()
|
||||
if err := repositories.Character.Save(ctx, character); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// 生效中:不给惩罚,也不写任何东西。
|
||||
arm(30 * time.Minute)
|
||||
level, exemption, err := service.commitCurrentForcedReturnWeakness(session)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if exemption != currentWeaknessExemptionPotItem {
|
||||
t.Fatalf("exemption=%q want %q", exemption, currentWeaknessExemptionPotItem)
|
||||
}
|
||||
if level != 0 || currentWeaknessStoredLevel(t, repositories) != 0 {
|
||||
t.Fatalf("生效期内不应写入惩罚: level=%d stored=%d", level, currentWeaknessStoredLevel(t, repositories))
|
||||
}
|
||||
|
||||
// 已过期:照常按满额惩罚。
|
||||
arm(-time.Minute)
|
||||
level, exemption, err = service.commitCurrentForcedReturnWeakness(session)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if exemption != currentWeaknessExemptionNone {
|
||||
t.Fatalf("过期窗口不应豁免: exemption=%q", exemption)
|
||||
}
|
||||
if level != currentWeaknessForcedReturnLevel ||
|
||||
currentWeaknessStoredLevel(t, repositories) != currentWeaknessForcedReturnLevel {
|
||||
t.Fatalf("过期后应写入满额惩罚: level=%d stored=%d", level, currentWeaknessStoredLevel(t, repositories))
|
||||
}
|
||||
}
|
||||
|
||||
// 持有型(十周年防虚弱青石):材料栏里带着就已经生效,不需要使用,也没有窗口。
|
||||
func TestForcedReturnIsExemptedWhileHoldingPassiveAntiWeaknessItem(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
service, session, repositories, _ := prepareCurrentWeaknessTest(t, 40, 0, 0, nil)
|
||||
|
||||
const heldItemID = int64(490008656)
|
||||
if !dnfweaknesspotion.HeldExempts(heldItemID) {
|
||||
t.Fatalf("持有人型清单缺少 %d", heldItemID)
|
||||
}
|
||||
if err := repositories.Inventory.Save(ctx, dnfrepo.InventoryRecord{
|
||||
CharacterID: "5",
|
||||
Slots: map[string]dnfrepo.ItemStack{
|
||||
"0:3": {ItemID: heldItemID, Count: 1},
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
level, exemption, err := service.commitCurrentForcedReturnWeakness(session)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if exemption != currentWeaknessExemptionPotHeld {
|
||||
t.Fatalf("exemption=%q want %q", exemption, currentWeaknessExemptionPotHeld)
|
||||
}
|
||||
if level != 0 || currentWeaknessStoredLevel(t, repositories) != 0 {
|
||||
t.Fatalf("持有期内不应写入惩罚: level=%d stored=%d", level, currentWeaknessStoredLevel(t, repositories))
|
||||
}
|
||||
}
|
||||
|
||||
// 同一张表必须覆盖用户报告过的那些版本,并且时长各不相同。
|
||||
func TestAntiWeaknessPotionFamilyCarriesPerItemDurations(t *testing.T) {
|
||||
for itemID, wantSeconds := range map[int64]int64{
|
||||
10087825: 1800, // 防虚弱药剂(本次报告的那一件)
|
||||
490003621: 600,
|
||||
490008213: 900,
|
||||
490007908: 3600,
|
||||
10005909: 1800, // 说明未写时长,取 [cool time]
|
||||
} {
|
||||
seconds, ok := dnfweaknesspotion.ImmunitySeconds(itemID)
|
||||
if !ok || seconds != wantSeconds {
|
||||
t.Fatalf("item=%d seconds=%d ok=%t want=%d", itemID, seconds, ok, wantSeconds)
|
||||
}
|
||||
}
|
||||
if seconds, ok := dnfweaknesspotion.ImmunitySeconds(1001); ok || seconds != 0 {
|
||||
t.Fatalf("家族外道具不应有时长: seconds=%d ok=%t", seconds, ok)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user