Files
90CNServer/server/internal/services/dnfbridge/companion_pure_server_injection_test.go
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

101 lines
3.9 KiB
Go

package dnfbridge
import (
"os"
"path/filepath"
"testing"
"time"
platformpvf "longheng.io/server/internal/platform/pvf"
)
// TestCompanionPureServerInjectionResolvesRealPVFAPC 验证「不改客户端」这条路的
// **最可能失败点**:服务端自建 APC 时需要从真实 Script.pvf 里解析
// AICharacter code 5405。解析不到就会让整次 planRuntimeDungeonExtendedActors
// 失败 —— 那会直接导致**副本进不去**,而不是"支援兵不出现"。
//
// 这是一个 opt-in 的真实资源测试(与树里其他 real-PVF 测试同一约定):
//
// set DNF_WORLDMAP_REAL_PVF_SMOKE=<Script.pvf 路径>
func TestCompanionPureServerInjectionResolvesRealPVFAPC(t *testing.T) {
pvfPath := os.Getenv("DNF_WORLDMAP_REAL_PVF_SMOKE")
if pvfPath == "" {
t.Skip("set DNF_WORLDMAP_REAL_PVF_SMOKE to the Script.pvf path to verify APC5405 resolution")
}
archive, err := platformpvf.Open(pvfPath)
if err != nil {
t.Fatal(err)
}
catalog, err := newPVFDungeonAICharacterCatalog(archive)
if err != nil {
t.Fatal(err)
}
definition, found, err := catalog.Find(currentDungeonCompanionP0APCCode)
if err != nil {
t.Fatalf("resolve companion APC %d: %v", currentDungeonCompanionP0APCCode, err)
}
if !found {
t.Fatalf("companion APC %d is absent from the deployed PVF", currentDungeonCompanionP0APCCode)
}
if definition.Level <= 0 {
t.Fatalf("companion APC %d has non-positive level %d", currentDungeonCompanionP0APCCode, definition.Level)
}
t.Logf("companion APC resolved code=%d level=%d", currentDungeonCompanionP0APCCode, definition.Level)
}
// TestCompanionPureServerInjectionAppendsFriendlyActorToStartMap 验证开关打开后
// 真的会往 START_MAP 计划里追加一条**友方** APC 行。
//
// 这不是重复 plan 层的测试:这里走的是「开关 → 计划」的完整接线,
// 也就是"支援兵到底会不会进副本"的决定性一步。
func TestCompanionPureServerInjectionAppendsFriendlyActorToStartMap(t *testing.T) {
// 用文件开关打开这条通道(不依赖环境变量,因为环境变量必须在进程
// 启动前设置,而本部署的登录器会复用已运行的服务端)。
//
// 这里**不改** TestMain 钉的共享路径之外的东西:换成本用例专属的
// 临时文件,跑完恢复,避免影响同包其他用例。
flagPath := filepath.Join(t.TempDir(), currentDungeonCompanionP0StartMapFlagFile)
companionStartMapFlagMu.Lock()
previousOverride := companionStartMapFlagPathOverride
companionStartMapFlagPathOverride = flagPath
companionStartMapFlagChecked = time.Time{}
companionStartMapFlagMu.Unlock()
t.Cleanup(func() {
companionStartMapFlagMu.Lock()
companionStartMapFlagPathOverride = previousOverride
companionStartMapFlagChecked = time.Time{}
companionStartMapFlagMu.Unlock()
})
if err := os.WriteFile(flagPath, []byte("1"), 0o644); err != nil {
t.Fatalf("create switch file %s: %v", flagPath, err)
}
if !currentDungeonCompanionP0StartMapEnabled() {
t.Fatalf("switch file %s did not enable the companion APC channel", flagPath)
}
plan := runtimeDungeonExtendedActorPlan{NextObjectKey: 7}
if err := appendCurrentDungeonCompanionP0StartMapActorForRun(
&plan,
&pvfDungeonAICharacterCatalog{},
true,
); err == nil {
// 空目录必须报错(找不到 5405),说明这条路真的在解析定义、
// 而不是无条件追加一行。
t.Fatal("empty AI character catalog must not silently add an APC row")
}
// 关掉开关后不得再追加。
planOff := runtimeDungeonExtendedActorPlan{NextObjectKey: 7}
if err := appendCurrentDungeonCompanionP0StartMapActorForRun(
&planOff,
&pvfDungeonAICharacterCatalog{},
false,
); err != nil {
t.Fatalf("disabled channel must be a no-op, got %v", err)
}
if len(planOff.Actors) != 0 || planOff.NextObjectKey != 7 {
t.Fatalf("disabled channel mutated the plan: actors=%d next=%d", len(planOff.Actors), planOff.NextObjectKey)
}
}