服务端(Go): - GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面 - GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新 - 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新) - 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍 - 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0) - 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算 启动器(Python/JS): - 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭 - GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐) - 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
101 lines
3.9 KiB
Go
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)
|
|
}
|
|
}
|