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

116 lines
6.2 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package dnfbridge
// ============================================================================
// 神秘异次元遗迹「双组计数下发」—— 已定位通道,但本树格式对不上,未实施
//
// 【本树真正的"计数器表"通道】dungeon_tower_settlement.go:
//
// currentAdventureTowerCountsMsgID = 1294 // 计数器表(class-0 通知)
// currentTowerCharacterEntryMsgID = 546 // 角色侧条目
//
// op1294 的 body 恰好 4 个 u8,由 writeCurrentAdventureTowerCounters 写出:
//
// TowerRemaining[绝望], TowerLimits[绝望], TowerRemaining[悲叹], TowerLimits[悲叹]
//
// op546 的 body 是 `u32 道具ID + u8 剩余`(11008 绝望塔 / 5115 悲叹塔),
// 每次发两条。op1294 重建 tooltip 时会清掉不在表里的行 —— 这正是报告里
// "原塔类行一并保留,避免替换整张计数表时清除原值"所指的那张表。
//
// 目前 op1294 只在**塔类通关**时发送(dungeon_settlement_result_send.go:139
// 的 `plan.TowerRewardMsgID != 0` 分支),所以进入 107/0 时客户端从未收到过
// 任何计数表。
//
// 【为什么没有实施】三处与报告不一致,凭猜下发会写坏客户端 UI 状态:
//
// 1. 报告说「按当前 EXE 的 op1331 读取器」发送;但本树
// dnfenum.CmdPacketAtPriest2Awakening = 1331,且**全代码库没有任何发送
// op1331 的代码**,客户端抓包也从未发出过 1331。
// 2. 报告要下发的是"账号总用量 + 各角色各图用量"两组;而 op1294 的 4 字节
// 结构放的是"塔类剩余/上限 × 2 座塔",**结构上装不下**另一组。
// 3. op16(选图请求,dungeoncmd.SelectDungeonRequest)字段已全部核对:
// DungeonID/Difficulty/EntryOption/SelectionMode/RuntimeState/RuntimeToken/
// Reserved/PartyState/LeaderObjectKey/SpecialMode,**没有次数字段** ——
// 说明次数只能是服务端单向下发,客户端不回报。
//
// 【下一步需要的证据】只要用户确认"进 107/0 停在选图页时界面上是否显示剩余
// 次数",就能判定:不显示 => 该客户端不需要这项,服务端计数已经生效(超额入场
// 会被 Preflight/Commit 拒绝);显示 => 确为 op1294 那张表,再按"追加两组字节"
// 试发并用明文探针观测界面变化。在拿到这个观察前不写猜测格式。
// ============================================================================
// ============================================================================
// 神秘异次元遗迹「保底奖励」—— 已查清:现有独立掉落机制已经覆盖,无需改动
//
// 五件奖励道具(PVF 实测,均在库):
//
// 490006024 闪耀的次元精华 stackable/490006001/chn_490006024.stk
// 490004769 绿色次元精华 stackable/490004001/chn_490004769.stk
// 490004770 红色次元精华 stackable/490004001/chn_490004770.stk
// 490004771 黄色次元精华 stackable/490004001/chn_490004771.stk
// 490004772 蓝色次元精华 stackable/490004001/chn_490004772.stk
//
// 【来源】etc/independent_drop.etc(服务端常量 currentDungeonIndependentDropPath
// = "Etc/Independent_Drop.etc",大小写不敏感)。该表的记录格式已由
// parseCurrentDungeonIndependentDrops 解析为 17 个字段:
//
// values[0] 记录类型(0=怪物, 1=APC)
// values[1] 对象代码(怪物号)
// values[2] 道具 ID
// values[3..7] 五档难度的概率
// values[8..12] 五档难度的判定次数
// values[13..15] 等级下限/上限/难度过滤
// values[16] list 标志
//
// 表里与本活动相关的条目(概率 1000000,恰好等于
// currentDungeonIndependentDropDenominator):
//
// 62040 490004769 概率 1000000×5 次数 1×5
// 110889 490004769 概率 1000000×5 次数 2×5
// 110890 490004769 概率 1000000×5 次数 1×5
// 110891 490004769 概率 1000000×5 次数 1×5
// 110880 490006024 概率 1000000×5 次数 1×5
// 110881 490006024 概率 1000000×5 次数 1×5
// 110920 490004770 概率 1000000×5 次数 4×5
// 110921 490004770 概率 1000000×5 次数 8×5
// 110925 490004771 概率 1000000×5 次数 10×5
// 110926 490004771 概率 1000000×5 次数 10×5
// 110927 490004771 概率 1000000×5 次数 40×5
// 110936 490004772 概率 1000000×5 次数 3×5 ← 现场实测属于副本 4910
// 110938 490004772 概率 1000000×5 次数 6×5
//
// 【为什么这就是"保底"而无需改代码】rollCurrentDungeonIndependentDropEntries
// 对 type 0 的判定是:
//
// probability := currentDungeonIndependentDropProbability(Probability[difficulty], bonusPercent)
// for attempt := 0; attempt < attempts; attempt++ {
// if probability <= rng.Next(currentDungeonIndependentDropDenominator) { continue }
// result = append(result, entry.ItemID)
// }
//
// rng.Next(1000000) 取值区间是 [0,999999],所以 probability == 1000000 时
// `probability <= rng.Next(...)` **恒为假**,每次判定都命中 —— 即 attempts 次
// 全部掉落。这些条目因此每次击杀都稳定产出固定数量,正是"保底奖励"。
//
// 现场佐证:探针曾记录 MOBLEVEL dungeon=4910 ... code=110936 ——
// 110936 正是掉落 490004772(蓝色次元精华)的那只怪,且它确实出现在 4910。
//
// 结论:**不需要为保底奖励新增任何服务端发放逻辑**(可继续遵守报告要求的
// "不新增自定义掉率或奖励")。此前"五图 dgn 无 reward/drop 段"的观察是对的,
// 但精华并不写在 dgn 里,而写在独立掉落表里 —— 这一点已在本次纠正。
// ============================================================================
// currentAbisGuaranteeRewardItemIDs 是五件保底奖励道具的 PVF 身份,以及它们在
// etc/independent_drop.etc 中对应的怪物掉落者。仅供核对,不参与任何发放路径
// (发放由既有的独立掉落链路完成)。
var currentAbisGuaranteeRewardItemIDs = [...]struct {
ItemID uint32
Name string
MonsterID int64
}{
{490006024, "闪耀的次元精华", 110880},
{490004769, "绿色次元精华", 110889},
{490004770, "红色次元精华", 110920},
{490004771, "黄色次元精华", 110925},
{490004772, "蓝色次元精华", 110936},
}