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
4.4 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.
// 本文件按 NoPack 公共频道 raid S2C handler 的读取顺序构造固定包体。
package raid
import "errors"
const (
// RaidOtherChannelUserinfoMsgID 对应 sub_1D57390: u32 count + repeat u32 key。
RaidOtherChannelUserinfoMsgID uint16 = 819
// RaidOtherChannelRequestJoinResultMsgID 对应 sub_1D573E0: u32,u32。
RaidOtherChannelRequestJoinResultMsgID uint16 = 820
// RaidOtherChannelResponseJoinMsgID 对应 sub_CC2480: u8,u8。
RaidOtherChannelResponseJoinMsgID uint16 = 821
// RaidOtherChannelListPageMsgID 对应 sub_1D4C060: raw[20], rows12, rows13。
RaidOtherChannelListPageMsgID uint16 = 0x374
// RaidPublicListMsgID 对应 sub_1D7AA10。它才是“寻找攻坚队”窗口的
// 攻坚队对象快照,不是 0x374 的跨频道通用页容器。
RaidPublicListMsgID uint16 = 0x02f1
// RaidPublicListPageSize 来自 sub_1B4B520 的 33 * page 请求偏移。
RaidPublicListPageSize = 33
)
var errRaidPublicListPageTooLarge = errors.New("raid public list page exceeds 33 objects")
// PublicRaidListPage is the exact class-0/op753 snapshot consumed by
// sub_1D7AA10: total matching rows, returned row offset, row count, then full
// sub_17B8910 raid objects followed by their one-byte member counts.
type PublicRaidListPage struct {
TotalCount uint32
Offset uint32
Objects []RaidObjectMode0
}
// BuildPublicRaidListBody builds the native “寻找攻坚队” object page.
func BuildPublicRaidListBody(page PublicRaidListPage) ([]byte, error) {
if len(page.Objects) > RaidPublicListPageSize {
return nil, errRaidPublicListPageTooLarge
}
var writer packetWriter
writer.writeUint32(page.TotalCount)
writer.writeUint32(page.Offset)
writer.writeUint32(uint32(len(page.Objects)))
for _, object := range page.Objects {
if len(object.Members) > MaxAttackPartyMembers {
return nil, errRaidMemberTooMany
}
if err := writeRaidObjectBase(&writer, object); err != nil {
return nil, err
}
writer.writeByte(byte(len(object.Members)))
}
return writer.bytes(), nil
}
// OtherChannelListPage 是 S2C 0x374 的保守构造输入。
//
// MCP 20260705 复核:sub_1D4C060 开头读取 raw[20],随后读取
// u32 count + repeat raw[12],后半段再读取 u32 count + repeat raw[13]。
// 当前只把它作为 831 公共频道列表请求之后的候选列表页回包构造器,
// 不在 handler 中自动发送。
//
// Rows12 会被完整拷贝进步长 12 的容器。
// Rows13 是 0x183/0x34C/0x8F 同族 72 字节列表项的压缩格式:
// +0 u32, +4 u16, +6 u16, +8 u8, +9 u8, +10 u8, +11 u8;
// +12 暂未见直接使用。和扩展格式相比,0x374 不携带写入 item+0x24 的额外 u16,
// 因此客户端把 item+0x24 固定为 0。
type OtherChannelListPage struct {
Header [20]byte
Rows12 [][12]byte
Rows13 [][13]byte
}
// BuildOtherChannelListPageBody 构造 S2C 0x374 公共频道/攻坚列表页候选包体。
func BuildOtherChannelListPageBody(page OtherChannelListPage) []byte {
var writer packetWriter
writer.writeRaw(page.Header[:])
writeCheckUserRows12(&writer, page.Rows12)
writer.writeUint32(uint32(len(page.Rows13)))
for _, row := range page.Rows13 {
writer.writeRaw(row[:])
}
return writer.bytes()
}
// BuildOtherChannelUserinfoBody 构造 S2C 819 公共频道用户 key 列表。
func BuildOtherChannelUserinfoBody(keys []uint32) []byte {
var writer packetWriter
writer.writeUint32(uint32(len(keys)))
for _, key := range keys {
writer.writeUint32(key)
}
return writer.bytes()
}
// BuildOtherChannelRequestJoinResultBody 构造 S2C 820 的两个 u32 字段。
//
// MCP 20260705 复核:sub_1D573E0 读两个 u32 后调用
// sub_1C5B070(type=3, valueA, valueB)。valueA 必须匹配客户端对象 +552,
// valueB 是累加量,达到对象 +556 阈值后才置完成标志。它不是通用成功 ACK。
func BuildOtherChannelRequestJoinResultBody(valueA uint32, valueB uint32) []byte {
var writer packetWriter
writer.writeUint32(valueA)
writer.writeUint32(valueB)
return writer.bytes()
}
// BuildOtherChannelResponseJoinBody 构造 S2C 821 响应加入结果。
//
// MCP 20260705 复核:sub_CC2480 把 flag 写到 UI 对象 292 的 +4(bool),
// 把 resultOrSlot 写到 +8,然后触发 UI 610 刷新;实际语义仍由 owner 决定。
func BuildOtherChannelResponseJoinBody(resultOrSlot byte, flag byte) []byte {
var writer packetWriter
writer.writeByte(resultOrSlot)
writer.writeByte(flag)
return writer.bytes()
}