服务端(Go): - GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面 - GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新 - 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新) - 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍 - 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0) - 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算 启动器(Python/JS): - 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭 - GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐) - 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
116 lines
4.4 KiB
Go
116 lines
4.4 KiB
Go
// 本文件按 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()
|
||
}
|