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,128 @@
|
||||
package protocol
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
const (
|
||||
ChannelHeaderSize = 13
|
||||
GameServerUpperHeaderSize = ChannelHeaderSize
|
||||
GameServerUpperHeaderSize16 = 16
|
||||
DefaultChannelClassification = 1
|
||||
)
|
||||
|
||||
// ChannelHeader 是客户端发来的 raw upper 13 字节包头。
|
||||
type ChannelHeader struct {
|
||||
Classification byte
|
||||
MsgID uint16
|
||||
Length uint32
|
||||
Checksum uint32
|
||||
Seq uint16
|
||||
}
|
||||
|
||||
type ChannelPacket struct {
|
||||
Header ChannelHeader
|
||||
Body []byte
|
||||
}
|
||||
|
||||
// BuildChannelPacket 封装客户端到服务端方向使用的 13 字节 raw upper 包。
|
||||
func BuildChannelPacket(msgID uint16, body []byte, seq uint16, classification byte) ([]byte, error) {
|
||||
if classification == 0 {
|
||||
classification = DefaultChannelClassification
|
||||
}
|
||||
total := ChannelHeaderSize + len(body)
|
||||
if total > int(^uint32(0)) {
|
||||
return nil, ErrPacketTooLarge
|
||||
}
|
||||
packet := make([]byte, total)
|
||||
packet[0] = classification
|
||||
binary.LittleEndian.PutUint16(packet[1:3], msgID)
|
||||
binary.LittleEndian.PutUint32(packet[3:7], uint32(total))
|
||||
binary.LittleEndian.PutUint16(packet[11:13], seq)
|
||||
copy(packet[ChannelHeaderSize:], body)
|
||||
sum, err := ChecksumRange(packet, 11, total-11)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
binary.LittleEndian.PutUint32(packet[7:11], sum)
|
||||
return packet, nil
|
||||
}
|
||||
|
||||
// BuildGameServerUpperPacket 封装 game 端口服务端发给客户端的 raw upper 包。
|
||||
// 最新 C# 重写文件与 7001 upper 一致使用 13 字节头;16 字节头只保留为兼容实验。
|
||||
func BuildGameServerUpperPacket(msgID uint16, body []byte, seq uint16, classification byte) ([]byte, error) {
|
||||
return BuildGameServerUpperPacketWithHeaderSize(msgID, body, seq, classification, GameServerUpperHeaderSize)
|
||||
}
|
||||
|
||||
// BuildGameServerUpperPacketWithHeaderSize 按指定头长封装 game raw upper。
|
||||
//
|
||||
// headerSize 只允许 13 或 16。16 字节模式会在 seq 后补 3 个保留字节,
|
||||
// 用于回退旧实验;主路径必须保持 13 字节,避免客户端按 ChannelPacket 读体时错位。
|
||||
func BuildGameServerUpperPacketWithHeaderSize(msgID uint16, body []byte, seq uint16, classification byte, headerSize int) ([]byte, error) {
|
||||
if headerSize != ChannelHeaderSize && headerSize != GameServerUpperHeaderSize16 {
|
||||
return nil, ErrPacketLength
|
||||
}
|
||||
total := headerSize + len(body)
|
||||
if total > int(^uint32(0)) {
|
||||
return nil, ErrPacketTooLarge
|
||||
}
|
||||
packet := make([]byte, total)
|
||||
packet[0] = classification
|
||||
binary.LittleEndian.PutUint16(packet[1:3], msgID)
|
||||
binary.LittleEndian.PutUint32(packet[3:7], uint32(total))
|
||||
binary.LittleEndian.PutUint16(packet[11:13], seq)
|
||||
copy(packet[headerSize:], body)
|
||||
sum, err := ChecksumRange(packet, 11, total-11)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
binary.LittleEndian.PutUint32(packet[7:11], sum)
|
||||
return packet, nil
|
||||
}
|
||||
|
||||
func ParseChannelPacket(packet []byte) (ChannelPacket, error) {
|
||||
return parseChannelPacket(packet, true)
|
||||
}
|
||||
|
||||
// ParseChannelPacketUnchecked 只按 13 字节 upper 包头切 body,不校验 checksum。
|
||||
//
|
||||
// 最新 game 端口上行的部分 raw upper 包会在 body 里携带 codec/encrypted payload,
|
||||
// 线上的 0x02B2 样本显示同 msg/length 下 body 每次变化但 header checksum 固定。
|
||||
// 分流阶段必须先保住 msg/length 语义,checksum 只能作为后续诊断信号。
|
||||
func ParseChannelPacketUnchecked(packet []byte) (ChannelPacket, error) {
|
||||
return parseChannelPacket(packet, false)
|
||||
}
|
||||
|
||||
func parseChannelPacket(packet []byte, verifyChecksum bool) (ChannelPacket, error) {
|
||||
if len(packet) < ChannelHeaderSize {
|
||||
return ChannelPacket{}, ErrPacketTooShort
|
||||
}
|
||||
header := ChannelHeader{
|
||||
Classification: packet[0],
|
||||
MsgID: binary.LittleEndian.Uint16(packet[1:3]),
|
||||
Length: binary.LittleEndian.Uint32(packet[3:7]),
|
||||
Checksum: binary.LittleEndian.Uint32(packet[7:11]),
|
||||
Seq: binary.LittleEndian.Uint16(packet[11:13]),
|
||||
}
|
||||
if header.Length < ChannelHeaderSize || int(header.Length) != len(packet) {
|
||||
return ChannelPacket{}, ErrPacketLength
|
||||
}
|
||||
if verifyChecksum {
|
||||
sum, err := ChecksumRange(packet, 11, len(packet)-11)
|
||||
if err != nil {
|
||||
return ChannelPacket{}, err
|
||||
}
|
||||
if sum != header.Checksum {
|
||||
return ChannelPacket{}, ErrChecksumInvalid
|
||||
}
|
||||
}
|
||||
return ChannelPacket{
|
||||
Header: header,
|
||||
Body: cloneBytes(packet[ChannelHeaderSize:]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func cloneBytes(data []byte) []byte {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
return append([]byte(nil), data...)
|
||||
}
|
||||
Reference in new issue
Block a user