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,332 @@
|
||||
package dnfbridge
|
||||
|
||||
import (
|
||||
dnfrepo "longheng.io/server/internal/modules/dnf/repository"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type userInfo23Scalar struct {
|
||||
family string
|
||||
scalarIndex int
|
||||
sourceOrder int
|
||||
value uint32
|
||||
}
|
||||
|
||||
type userInfo23Pair struct {
|
||||
section string
|
||||
key uint32
|
||||
value uint32
|
||||
}
|
||||
|
||||
type userInfo23Fixed struct {
|
||||
section string
|
||||
slot int
|
||||
value uint32
|
||||
}
|
||||
|
||||
type userInfoSlotEntry struct {
|
||||
groupID int
|
||||
category int
|
||||
key uint32
|
||||
value uint32
|
||||
}
|
||||
|
||||
func buildCSharpLegacyUserInfo23FallbackBody() []byte {
|
||||
// 0x23 是当前 USERINFO 主通知。legacy seed 表为空时仍发送空结构,避免选择角色阶段 deferred 后进场景完全缺包。
|
||||
reader := csharpLegacyUserInfoReader{}
|
||||
return reader.build23()
|
||||
}
|
||||
|
||||
func (r *csharpLegacyUserInfoReader) build23() []byte {
|
||||
scalars := r.load23Scalars()
|
||||
fixed := r.load23Fixed()
|
||||
pairs := r.load23Pairs()
|
||||
objects := r.rows("legacy_character_userinfo23_object_rows",
|
||||
[]string{"sort_order", "object_or_slot_id", "value_a", "value_b", "value_c", "value_d", "value_e"},
|
||||
[]string{"sort_order"})
|
||||
control := r.one("legacy_character_userinfo_slot_control",
|
||||
[]string{"group0_tail_refresh_value", "mode_bits", "tail_value_a", "tail_flag_a", "tail_bool_b", "tail_value_b", "tail_pair_a", "tail_pair_b", "tail_index6_value"})
|
||||
slotEntries := r.loadSlotEntries()
|
||||
|
||||
var w packetWriter
|
||||
write23U32(&w, scalars, 1, pick23(scalars, 0, ref23("direct", 401)))
|
||||
write23U32(&w, scalars, 2, pick23(scalars, 0, ref23("e90", 1)))
|
||||
write23U32(&w, scalars, 3, pick23(scalars, 0, ref23("e90", 2)))
|
||||
write23U32(&w, scalars, 4, pick23(scalars, 0, ref23("e90", 3), ref23("db0", 0)))
|
||||
write23U8(&w, scalars, 5, pick23(scalars, 0, ref23("direct", 399)))
|
||||
write23U32(&w, scalars, 6, pick23(scalars, 0, ref23("e90", 6), ref23("db0", 9)))
|
||||
write23U32(&w, scalars, 7, pick23(scalars, 0, ref23("direct", 397), ref23("db0", 7)))
|
||||
write23U32(&w, scalars, 8, pick23(scalars, 0, ref23("e90", 5), ref23("db0", 2)))
|
||||
write23U32(&w, scalars, 9, pick23(scalars, 0, ref23("e90", 9), ref23("db0", 13)))
|
||||
write23U32(&w, scalars, 10, pick23(scalars, 0, ref23("direct", 398)))
|
||||
write23U32(&w, scalars, 11, pick23(scalars, 0, ref23("e90", 4), ref23("db0", 1)))
|
||||
write23U32(&w, scalars, 12, pick23(scalars, 0, ref23("e90", 8)))
|
||||
// C# UserInfo23BodyBuilder 是当前 0x0023 的字节顺序证据;这里不能沿用旧提取的 d90/db0 顺序。
|
||||
write23U32(&w, scalars, 13, pick23(scalars, 0, ref23("e90", 10), ref23("db0", 6)))
|
||||
write23U32(&w, scalars, 14, pick23(scalars, 0, ref23("e90", 11)))
|
||||
write23U32(&w, scalars, 15, pick23(scalars, 0, ref23("e90", 12), ref23("db0", 10)))
|
||||
write23U32(&w, scalars, 16, pick23(scalars, 0, ref23("e90", 13)))
|
||||
write23U32(&w, scalars, 17, pick23(scalars, 0, ref23("e90", 14)))
|
||||
write23U32(&w, scalars, 18, pick23(scalars, 0, ref23("db0", 5)))
|
||||
write23U32(&w, scalars, 19, pick23(scalars, 0, ref23("d90", 2)))
|
||||
write23U32(&w, scalars, 20, pick23(scalars, 0, ref23("d90", 10)))
|
||||
write23U32(&w, scalars, 21, computeAttr268Index7Remainder(scalars))
|
||||
write23U32(&w, scalars, 22, pick23(scalars, 0, ref23("d90", 4)))
|
||||
write23U32(&w, scalars, 23, pick23(scalars, 0, ref23("d90", 5)))
|
||||
write23U32(&w, scalars, 24, pick23(scalars, 0, ref23("d90", 8)))
|
||||
write23U32(&w, scalars, 25, pick23(scalars, 0, ref23("d90", 9)))
|
||||
write23U32(&w, scalars, 26, pick23(scalars, 0, ref23("d90", 6)))
|
||||
write23U32(&w, scalars, 27, pick23(scalars, 0, ref23("d90", 3)))
|
||||
write23U32(&w, scalars, 28, 0)
|
||||
write23U32(&w, scalars, 29, pick23(scalars, 0, ref23("d90", 0)))
|
||||
write23U32(&w, scalars, 30, pick23(scalars, 0, ref23("d90", 1)))
|
||||
write23PairSectionU8(&w, scalars, pairs, "d90_ext", ref23("d90", 11))
|
||||
write23PairSectionU8(&w, scalars, pairs, "db0_ext", ref23("db0", 11))
|
||||
write23U32(&w, scalars, 31, pick23(scalars, 0, ref23("e90", 16)))
|
||||
write23U32(&w, scalars, 32, pick23(scalars, 0, ref23("e90", 17)))
|
||||
write23U32(&w, scalars, 33, pick23(scalars, 0, ref23("e90", 18)))
|
||||
write23U32(&w, scalars, 34, pick23(scalars, 0, ref23("e90", 19)))
|
||||
write23U32(&w, scalars, 35, pick23(scalars, 0, ref23("e90", 20), ref23("db0", 14)))
|
||||
write23U32(&w, scalars, 36, pick23(scalars, 0, ref23("d90", 13)))
|
||||
write23U32(&w, scalars, 37, pick23(scalars, 0, ref23("e90", 21), ref23("db0", 15)))
|
||||
write23U32(&w, scalars, 38, pick23(scalars, 0, ref23("e90", 22)))
|
||||
for i := 0; i < 4; i++ {
|
||||
w.writeUint32(fixed23(fixed, "fixed4", i))
|
||||
}
|
||||
write23PairSectionU32(&w, pairs, "map")
|
||||
write23ObjectRows(&w, objects)
|
||||
write23SlotGroup(&w, slotEntries, 0)
|
||||
w.writeUint32(rowU32(control, "group0_tail_refresh_value", 0))
|
||||
write23SlotGroup(&w, slotEntries, 1)
|
||||
write23SlotGroup(&w, slotEntries, 2)
|
||||
write23U32(&w, scalars, 39, rowU32(control, "mode_bits", 0))
|
||||
write23U32(&w, scalars, 40, rowU32(control, "tail_value_a", 0))
|
||||
write23U8(&w, scalars, 41, uint32(rowU8(control, "tail_flag_a", 0)))
|
||||
write23U8(&w, scalars, 42, uint32(rowU8(control, "tail_bool_b", 0)))
|
||||
write23U32(&w, scalars, 43, rowU32(control, "tail_value_b", 0))
|
||||
write23U32(&w, scalars, 44, rowU32(control, "tail_pair_a", 0))
|
||||
write23U32(&w, scalars, 45, rowU32(control, "tail_pair_b", 0))
|
||||
write23U32(&w, scalars, 46, rowU32(control, "tail_index6_value", 0))
|
||||
return w.bytes()
|
||||
}
|
||||
|
||||
func (r *csharpLegacyUserInfoReader) load23Scalars() []userInfo23Scalar {
|
||||
rows := r.rows("legacy_character_userinfo23_scalar_values",
|
||||
[]string{"family", "scalar_index", "source_order", "value"},
|
||||
[]string{"source_order"})
|
||||
out := make([]userInfo23Scalar, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
out = append(out, userInfo23Scalar{
|
||||
family: strings.ToLower(rowString(row, "family", "")),
|
||||
scalarIndex: rowInt(row, "scalar_index", 0),
|
||||
sourceOrder: rowInt(row, "source_order", 0),
|
||||
value: rowU32(row, "value", 0),
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (r *csharpLegacyUserInfoReader) load23Fixed() []userInfo23Fixed {
|
||||
rows := r.rows("legacy_character_userinfo23_fixed_values",
|
||||
[]string{"section", "slot_index", "value"},
|
||||
[]string{"section", "slot_index"})
|
||||
out := make([]userInfo23Fixed, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
out = append(out, userInfo23Fixed{
|
||||
section: strings.ToLower(rowString(row, "section", "")),
|
||||
slot: rowInt(row, "slot_index", 0),
|
||||
value: rowU32(row, "value", 0),
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (r *csharpLegacyUserInfoReader) load23Pairs() []userInfo23Pair {
|
||||
rows := r.rows("legacy_character_userinfo23_pair_entries",
|
||||
[]string{"section", "entry_key", "value"},
|
||||
[]string{"section", "sort_order"})
|
||||
out := make([]userInfo23Pair, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
out = append(out, userInfo23Pair{
|
||||
section: strings.ToLower(rowString(row, "section", "")),
|
||||
key: rowU32(row, "entry_key", 0),
|
||||
value: rowU32(row, "value", 0),
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (r *csharpLegacyUserInfoReader) loadSlotEntries() []userInfoSlotEntry {
|
||||
rows := r.rows("legacy_character_userinfo_slot_group_entries",
|
||||
[]string{"group_id", "category", "key_or_item_id", "value"},
|
||||
[]string{"group_id", "category", "sort_order"})
|
||||
out := make([]userInfoSlotEntry, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
out = append(out, userInfoSlotEntry{
|
||||
groupID: rowInt(row, "group_id", 0),
|
||||
category: rowInt(row, "category", 0),
|
||||
key: rowU32(row, "key_or_item_id", 0),
|
||||
value: rowU32(row, "value", 0),
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
type scalarRef23 struct {
|
||||
family string
|
||||
index int
|
||||
}
|
||||
|
||||
func ref23(family string, index int) scalarRef23 {
|
||||
return scalarRef23{family: strings.ToLower(family), index: index}
|
||||
}
|
||||
|
||||
func write23U32(w *packetWriter, scalars []userInfo23Scalar, order int, fallback uint32) {
|
||||
w.writeUint32(valueByOrder23(scalars, order, fallback))
|
||||
}
|
||||
|
||||
func write23U8(w *packetWriter, scalars []userInfo23Scalar, order int, fallback uint32) {
|
||||
value := valueByOrder23(scalars, order, fallback)
|
||||
if value > 0xff {
|
||||
value = 0xff
|
||||
}
|
||||
w.writeByte(byte(value))
|
||||
}
|
||||
|
||||
func valueByOrder23(scalars []userInfo23Scalar, order int, fallback uint32) uint32 {
|
||||
for _, scalar := range scalars {
|
||||
if scalar.sourceOrder == order {
|
||||
return scalar.value
|
||||
}
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
func pick23(scalars []userInfo23Scalar, fallback uint32, refs ...scalarRef23) uint32 {
|
||||
for _, ref := range refs {
|
||||
if value, ok := scalar23(scalars, ref.family, ref.index); ok {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
func scalar23(scalars []userInfo23Scalar, family string, index int) (uint32, bool) {
|
||||
family = strings.ToLower(family)
|
||||
for _, scalar := range scalars {
|
||||
if scalar.family == family && scalar.scalarIndex == index {
|
||||
return scalar.value, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func computeAttr268Index7Remainder(scalars []userInfo23Scalar) uint32 {
|
||||
if value, ok := valueByOrder23OK(scalars, 21); ok {
|
||||
return value
|
||||
}
|
||||
total, ok := scalar23(scalars, "d90", 7)
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
first := pick23(scalars, 0, ref23("e90", 11))
|
||||
if total > first {
|
||||
return total - first
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func valueByOrder23OK(scalars []userInfo23Scalar, order int) (uint32, bool) {
|
||||
for _, scalar := range scalars {
|
||||
if scalar.sourceOrder == order {
|
||||
return scalar.value, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func fixed23(values []userInfo23Fixed, section string, slot int) uint32 {
|
||||
section = strings.ToLower(section)
|
||||
for _, value := range values {
|
||||
if value.section == section && value.slot == slot {
|
||||
return value.value
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func write23PairSectionU8(w *packetWriter, scalars []userInfo23Scalar, pairs []userInfo23Pair, section string, synthetic scalarRef23) {
|
||||
entries := collect23Pairs(pairs, section)
|
||||
if len(entries) == 0 {
|
||||
if value, ok := scalar23(scalars, synthetic.family, synthetic.index); ok && value != 0 {
|
||||
entries = append(entries, userInfo23Pair{key: 1, value: value})
|
||||
}
|
||||
}
|
||||
if len(entries) > 0xff {
|
||||
entries = entries[:0xff]
|
||||
}
|
||||
w.writeByte(byte(len(entries)))
|
||||
for _, entry := range entries {
|
||||
w.writeUint32(entry.key)
|
||||
w.writeUint32(entry.value)
|
||||
}
|
||||
}
|
||||
|
||||
func write23PairSectionU32(w *packetWriter, pairs []userInfo23Pair, section string) {
|
||||
entries := collect23Pairs(pairs, section)
|
||||
w.writeUint32(uint32(len(entries)))
|
||||
for _, entry := range entries {
|
||||
w.writeUint32(entry.key)
|
||||
w.writeUint32(entry.value)
|
||||
}
|
||||
}
|
||||
|
||||
func collect23Pairs(pairs []userInfo23Pair, section string) []userInfo23Pair {
|
||||
section = strings.ToLower(section)
|
||||
out := make([]userInfo23Pair, 0)
|
||||
for _, entry := range pairs {
|
||||
if entry.section == section {
|
||||
out = append(out, entry)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func write23ObjectRows(w *packetWriter, rows []dnfrepo.LegacyUserInfoRow) {
|
||||
if len(rows) > 0xff {
|
||||
rows = rows[:0xff]
|
||||
}
|
||||
w.writeByte(byte(len(rows)))
|
||||
for _, row := range rows {
|
||||
w.writeUint16(rowU16(row, "object_or_slot_id", 0))
|
||||
w.writeUint32(rowU32(row, "value_a", 0))
|
||||
w.writeUint32(rowU32(row, "value_b", 0))
|
||||
w.writeUint16(rowU16(row, "value_c", 0))
|
||||
w.writeByte(rowU8(row, "value_d", 0))
|
||||
w.writeUint16(rowU16(row, "value_e", 0))
|
||||
}
|
||||
}
|
||||
|
||||
func write23SlotGroup(w *packetWriter, entries []userInfoSlotEntry, groupID int) {
|
||||
for category := 0; category < 8; category++ {
|
||||
group := collectSlotEntries(entries, groupID, category)
|
||||
if len(group) > 0xff {
|
||||
group = group[:0xff]
|
||||
}
|
||||
w.writeByte(byte(len(group)))
|
||||
for _, entry := range group {
|
||||
w.writeUint32(entry.key)
|
||||
w.writeUint32(entry.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func collectSlotEntries(entries []userInfoSlotEntry, groupID int, category int) []userInfoSlotEntry {
|
||||
out := make([]userInfoSlotEntry, 0)
|
||||
for _, entry := range entries {
|
||||
if entry.groupID == groupID && entry.category == category {
|
||||
out = append(out, entry)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in new issue
Block a user