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,297 @@
|
||||
// 本文件解析攻坚队管理类 C2S 请求体。
|
||||
package raid
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// InfoRequest 是 C2S 0x298/664 创建攻坚队和 0x29E/670 修改攻坚队信息的共用包体。
|
||||
// MCP 证据:sub_1B49240 先写 u8,再写 dstr 名称,最后写 u8。
|
||||
type InfoRequest struct {
|
||||
RouteOrRaidType byte
|
||||
NameBytes []byte
|
||||
TailFlag byte
|
||||
}
|
||||
|
||||
// RequestRaidInfoRequest 是 C2S 0x54F/1359 的请求体:u8 mode + u32 raid_key。
|
||||
//
|
||||
// 证据:客户端组包点 sub_17B1A90 依次 push 0x54F、一个字节 2、以及 raid_key,
|
||||
// 实测正文 `01 01 00 17 00` 的后四字节 0x00170001 正是当前攻坚队的 key。
|
||||
// (旧注释曾把后两个字节当成频道号,那是因为 raid_key 的高 16 位恰好就是频道。)
|
||||
// S2C 1359 的当前 EXE handler sub_1D3A150 不读取正文,只把本地对象的 +0xE1E 置 1。
|
||||
type RequestRaidInfoRequest struct {
|
||||
Mode byte
|
||||
RaidKey uint32
|
||||
Raw [5]byte
|
||||
}
|
||||
|
||||
// LeaveRequest 是 C2S 0x299/665 离开攻坚队包体。
|
||||
// MCP 证据:sub_17B1E30/sub_324F850 只写一个 u16。
|
||||
type LeaveRequest struct {
|
||||
RaidOrMemberKey uint16
|
||||
}
|
||||
|
||||
// RejoinRequest 是 C2S 0x29C/668 重新加入攻坚链路的客户端回包。
|
||||
// MCP 证据:sub_1D638D0 的触发分支只回写一个 u32。
|
||||
type RejoinRequest struct {
|
||||
RaidKey uint32
|
||||
}
|
||||
|
||||
// SetWaitingRequest 是 C2S 0x29B/667 的攻坚等待状态请求。
|
||||
// MCP 证据:sub_1B4C2A0 分支发送 u8 0,再发送 u8 sub_17B1CD0(...)。
|
||||
type SetWaitingRequest struct {
|
||||
Flag byte
|
||||
RouteRaidType byte
|
||||
}
|
||||
|
||||
// EntryCostInfoRequest 是 C2S 0x292/658 的 raid 入场材料/消耗信息请求。
|
||||
// MCP 20260705 证据:sub_17B19C0 发送 658,正文只写 u8(a2 != 0)。
|
||||
type EntryCostInfoRequest struct {
|
||||
Enabled byte
|
||||
}
|
||||
|
||||
// SetSymbolRequest 是 C2S 0x296/662 的 raid 标记设置请求。
|
||||
// MCP 20260705 证据:sub_17B1600 发送 662,正文写 u32,u32,u8;第三字段必须小于 3。
|
||||
type SetSymbolRequest struct {
|
||||
SourceValue uint32
|
||||
TargetValue uint32
|
||||
Symbol byte
|
||||
}
|
||||
|
||||
// ManagerWorkRequest 是 C2S 0x29D/669 的小组编辑请求。
|
||||
type ManagerWorkRequest struct {
|
||||
ActionOrMode uint32
|
||||
MemberCharKey uint32
|
||||
TargetGroup uint32
|
||||
}
|
||||
|
||||
// OtherChannelRequestJoinRequest 是 C2S 0x334/820 的跨频道攻坚申请。
|
||||
// MCP 证据:sub_17B1530 写 u8,u16,u32,u16。
|
||||
type OtherChannelRequestJoinRequest struct {
|
||||
Mode byte
|
||||
TargetKey uint16
|
||||
ClientValue uint32
|
||||
RouteRaidType uint16
|
||||
}
|
||||
|
||||
// MemberChangeStateRequest 是 C2S 0x337/823 的成员状态变化请求。
|
||||
// MCP 证据:sub_17B1C40 只写 u8 state。
|
||||
type MemberChangeStateRequest struct {
|
||||
State byte
|
||||
}
|
||||
|
||||
// UserMoveChannelFailRequest 是 C2S 0x338/824 的跨频道移动失败上报。
|
||||
// MCP 证据:sub_17B17A0 写 u8,u16。
|
||||
type UserMoveChannelFailRequest struct {
|
||||
Mode byte
|
||||
TargetKey uint16
|
||||
}
|
||||
|
||||
// OtherChannelListRequest 是 C2S 0x33F/831 的公共频道攻坚列表请求。
|
||||
// MCP 证据:sub_17B3140 写 u8=1,或 u8=0 + raw[8] + dstr name。
|
||||
type OtherChannelListRequest struct {
|
||||
Mode byte
|
||||
Context [8]byte
|
||||
NameBytes []byte
|
||||
HasContext bool
|
||||
HasNameDstr bool
|
||||
}
|
||||
|
||||
// CheckRaidUserRequest 是 C2S 0x379/889 的攻坚用户检查请求。
|
||||
// MCP 证据:sub_17B1D50 写 u8,u16;S2C 889 是复杂列表,不在解析层伪造成功。
|
||||
type CheckRaidUserRequest struct {
|
||||
Mode byte
|
||||
TargetKey uint16
|
||||
}
|
||||
|
||||
var errRaidInfoBodyShort = errors.New("raid info body too short")
|
||||
var errRaidInfoNameTruncated = errors.New("raid info name dstr truncated")
|
||||
var errRaidRequestInfoBodyShort = errors.New("raid request info body must be five bytes")
|
||||
var errRaidLeaveBodyShort = errors.New("raid leave body too short")
|
||||
var errRaidStartBodyNotEmpty = errors.New("raid start body must be empty")
|
||||
var errRaidRejoinBodyShort = errors.New("raid rejoin body too short")
|
||||
var errRaidSetWaitingBodyShort = errors.New("raid set waiting body too short")
|
||||
var errRaidEntryCostBodyShort = errors.New("raid entry cost info body too short")
|
||||
var errRaidSetSymbolBodyShort = errors.New("raid set symbol body too short")
|
||||
var errRaidSetSymbolInvalid = errors.New("raid set symbol must be less than 3")
|
||||
var errManagerWorkBodyShort = errors.New("raid manager work body too short")
|
||||
var errRaidOtherChannelJoinBodyShort = errors.New("raid other channel request join body too short")
|
||||
var errRaidMemberStateBodyShort = errors.New("raid member change state body too short")
|
||||
var errRaidMoveChannelFailBodyShort = errors.New("raid user move channel fail body too short")
|
||||
var errRaidOtherChannelListBodyShort = errors.New("raid other channel list body too short")
|
||||
var errRaidOtherChannelListNameTruncated = errors.New("raid other channel list name dstr truncated")
|
||||
var errRaidCheckUserBodyShort = errors.New("raid check user body too short")
|
||||
|
||||
// DecodeCreateRaidRequest 按当前 EXE 写包顺序解析创建攻坚队请求。
|
||||
func DecodeCreateRaidRequest(body []byte) (InfoRequest, error) {
|
||||
return decodeInfoRequest(body)
|
||||
}
|
||||
|
||||
// DecodeModifyRaidInfoRequest 按当前 EXE 写包顺序解析修改攻坚队信息请求。
|
||||
func DecodeModifyRaidInfoRequest(body []byte) (InfoRequest, error) {
|
||||
return decodeInfoRequest(body)
|
||||
}
|
||||
|
||||
// DecodeRequestRaidInfoRequest 解析当前客户端发送的 1359 请求。
|
||||
//
|
||||
// 布局 u8 mode + u32 raid_key 来自组包点 sub_17B1A90(push 0x54F / push 2 /
|
||||
// push raid_key)。实测样本的 mode 为 1 或 2:mode 1 是打开攻坚队窗口时的常规
|
||||
// 刷新,mode 2 是客户端刚为某个 key 新建了对象、回头向服务端索取队信息。
|
||||
func DecodeRequestRaidInfoRequest(body []byte) (RequestRaidInfoRequest, error) {
|
||||
if len(body) != 5 {
|
||||
return RequestRaidInfoRequest{}, errRaidRequestInfoBodyShort
|
||||
}
|
||||
var request RequestRaidInfoRequest
|
||||
copy(request.Raw[:], body)
|
||||
request.Mode = body[0]
|
||||
request.RaidKey = binary.LittleEndian.Uint32(body[1:5])
|
||||
return request, nil
|
||||
}
|
||||
|
||||
func decodeInfoRequest(body []byte) (InfoRequest, error) {
|
||||
if len(body) < 6 {
|
||||
return InfoRequest{}, errRaidInfoBodyShort
|
||||
}
|
||||
nameLen := int(binary.LittleEndian.Uint32(body[1:5]))
|
||||
end := 5 + nameLen
|
||||
if end+1 != len(body) {
|
||||
return InfoRequest{}, errRaidInfoNameTruncated
|
||||
}
|
||||
return InfoRequest{
|
||||
RouteOrRaidType: body[0],
|
||||
NameBytes: append([]byte(nil), body[5:end]...),
|
||||
TailFlag: body[end],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DecodeLeaveRaidRequest parses the exact u16 body written by the current EXE.
|
||||
func DecodeLeaveRaidRequest(body []byte) (LeaveRequest, error) {
|
||||
if len(body) != 2 {
|
||||
return LeaveRequest{}, errRaidLeaveBodyShort
|
||||
}
|
||||
return LeaveRequest{RaidOrMemberKey: binary.LittleEndian.Uint16(body[0:2])}, nil
|
||||
}
|
||||
|
||||
// DecodeStartRaidRequest 校验开始攻坚请求;当前 EXE 没有写任何正文。
|
||||
func DecodeStartRaidRequest(body []byte) error {
|
||||
if len(body) != 0 {
|
||||
return errRaidStartBodyNotEmpty
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecodeRejoinRaidRequest 解析重新加入攻坚请求。
|
||||
func DecodeRejoinRaidRequest(body []byte) (RejoinRequest, error) {
|
||||
if len(body) != 4 {
|
||||
return RejoinRequest{}, errRaidRejoinBodyShort
|
||||
}
|
||||
return RejoinRequest{RaidKey: binary.LittleEndian.Uint32(body[0:4])}, nil
|
||||
}
|
||||
|
||||
// DecodeSetWaitingRequest 解析 667 攻坚等待状态请求。
|
||||
func DecodeSetWaitingRequest(body []byte) (SetWaitingRequest, error) {
|
||||
if len(body) != 2 {
|
||||
return SetWaitingRequest{}, errRaidSetWaitingBodyShort
|
||||
}
|
||||
return SetWaitingRequest{Flag: body[0], RouteRaidType: body[1]}, nil
|
||||
}
|
||||
|
||||
// DecodeEntryCostInfoRequest 解析 658 raid 入场消耗信息请求。
|
||||
func DecodeEntryCostInfoRequest(body []byte) (EntryCostInfoRequest, error) {
|
||||
if len(body) != 1 {
|
||||
return EntryCostInfoRequest{}, errRaidEntryCostBodyShort
|
||||
}
|
||||
return EntryCostInfoRequest{Enabled: body[0]}, nil
|
||||
}
|
||||
|
||||
// DecodeSetSymbolRequest 解析 662 raid 标记设置请求。
|
||||
func DecodeSetSymbolRequest(body []byte) (SetSymbolRequest, error) {
|
||||
if len(body) != 9 {
|
||||
return SetSymbolRequest{}, errRaidSetSymbolBodyShort
|
||||
}
|
||||
if body[8] >= 3 {
|
||||
return SetSymbolRequest{}, errRaidSetSymbolInvalid
|
||||
}
|
||||
return SetSymbolRequest{
|
||||
SourceValue: binary.LittleEndian.Uint32(body[0:4]),
|
||||
TargetValue: binary.LittleEndian.Uint32(body[4:8]),
|
||||
Symbol: body[8],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DecodeManagerWorkRequest 按 NoPack sub_1B4D3D0/sub_1B480E0 的发送顺序解析 669。
|
||||
func DecodeManagerWorkRequest(body []byte) (ManagerWorkRequest, error) {
|
||||
if len(body) != 12 {
|
||||
return ManagerWorkRequest{}, errManagerWorkBodyShort
|
||||
}
|
||||
return ManagerWorkRequest{
|
||||
ActionOrMode: binary.LittleEndian.Uint32(body[0:4]),
|
||||
MemberCharKey: binary.LittleEndian.Uint32(body[4:8]),
|
||||
TargetGroup: binary.LittleEndian.Uint32(body[8:12]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DecodeOtherChannelRequestJoinRequest 解析 820 跨频道攻坚申请请求。
|
||||
func DecodeOtherChannelRequestJoinRequest(body []byte) (OtherChannelRequestJoinRequest, error) {
|
||||
if len(body) != 9 {
|
||||
return OtherChannelRequestJoinRequest{}, errRaidOtherChannelJoinBodyShort
|
||||
}
|
||||
return OtherChannelRequestJoinRequest{
|
||||
Mode: body[0],
|
||||
TargetKey: binary.LittleEndian.Uint16(body[1:3]),
|
||||
ClientValue: binary.LittleEndian.Uint32(body[3:7]),
|
||||
RouteRaidType: binary.LittleEndian.Uint16(body[7:9]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DecodeMemberChangeStateRequest 解析 823 攻坚成员状态变化请求。
|
||||
func DecodeMemberChangeStateRequest(body []byte) (MemberChangeStateRequest, error) {
|
||||
if len(body) != 1 {
|
||||
return MemberChangeStateRequest{}, errRaidMemberStateBodyShort
|
||||
}
|
||||
return MemberChangeStateRequest{State: body[0]}, nil
|
||||
}
|
||||
|
||||
// DecodeUserMoveChannelFailRequest 解析 824 跨频道移动失败上报。
|
||||
func DecodeUserMoveChannelFailRequest(body []byte) (UserMoveChannelFailRequest, error) {
|
||||
if len(body) != 3 {
|
||||
return UserMoveChannelFailRequest{}, errRaidMoveChannelFailBodyShort
|
||||
}
|
||||
return UserMoveChannelFailRequest{Mode: body[0], TargetKey: binary.LittleEndian.Uint16(body[1:3])}, nil
|
||||
}
|
||||
|
||||
// DecodeOtherChannelListRequest 解析 831 公共频道攻坚列表请求。
|
||||
func DecodeOtherChannelListRequest(body []byte) (OtherChannelListRequest, error) {
|
||||
if len(body) < 1 {
|
||||
return OtherChannelListRequest{}, errRaidOtherChannelListBodyShort
|
||||
}
|
||||
out := OtherChannelListRequest{Mode: body[0]}
|
||||
if out.Mode != 0 {
|
||||
if len(body) != 1 {
|
||||
return OtherChannelListRequest{}, errRaidOtherChannelListBodyShort
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
if len(body) < 13 {
|
||||
return OtherChannelListRequest{}, errRaidOtherChannelListBodyShort
|
||||
}
|
||||
copy(out.Context[:], body[1:9])
|
||||
out.HasContext = true
|
||||
nameLen := int(binary.LittleEndian.Uint32(body[9:13]))
|
||||
end := 13 + nameLen
|
||||
if end != len(body) {
|
||||
return OtherChannelListRequest{}, errRaidOtherChannelListNameTruncated
|
||||
}
|
||||
out.NameBytes = append([]byte(nil), body[13:end]...)
|
||||
out.HasNameDstr = true
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// DecodeCheckRaidUserRequest 解析 889 攻坚用户检查请求。
|
||||
func DecodeCheckRaidUserRequest(body []byte) (CheckRaidUserRequest, error) {
|
||||
if len(body) != 3 {
|
||||
return CheckRaidUserRequest{}, errRaidCheckUserBodyShort
|
||||
}
|
||||
return CheckRaidUserRequest{Mode: body[0], TargetKey: binary.LittleEndian.Uint16(body[1:3])}, nil
|
||||
}
|
||||
Reference in new issue
Block a user