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,100 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user