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,238 @@
|
||||
package dnfbridge
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"longheng.io/server/internal/modules/dnf/dnfenum"
|
||||
dnfrepo "longheng.io/server/internal/modules/dnf/repository"
|
||||
)
|
||||
|
||||
// sendSelectedEquippedCreatureItemRefresh replays only the existing creature
|
||||
// object in actor equipment slot 26. Full mode1 and op13 create that object,
|
||||
// but the current EXE applies a creature enchant's dynamic card state only on
|
||||
// its same-template op14 path. Never widen this to generic list-3 equipment:
|
||||
// ordinary equipment updates have different native lifetime requirements.
|
||||
func (s *Service) sendSelectedEquippedCreatureItemRefresh(session *gameSession, source string) error {
|
||||
if session == nil || session.selectedCharacterID == 0 {
|
||||
return fmt.Errorf("equipped creature item refresh requires an active character")
|
||||
}
|
||||
repositories, ok := s.repositoryGroup()
|
||||
if !ok || repositories.Equipment == nil {
|
||||
return fmt.Errorf("equipped creature item refresh repository is unavailable")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), currentRepositorySnapshotTimeout)
|
||||
defer cancel()
|
||||
characterID := strconv.Itoa(int(session.selectedCharacterID))
|
||||
record, found, err := repositories.Equipment.Load(ctx, characterID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("load equipped creature item refresh: %w", err)
|
||||
}
|
||||
equipped, worn := currentEquippedCreatureEquipmentEntry(record)
|
||||
if !found || !worn {
|
||||
s.logPacketEvent("game-upper-current-equipped-creature-item-refresh-skipped",
|
||||
"conn_id", session.connID,
|
||||
"channel_id", session.channel.ID,
|
||||
"source", source,
|
||||
"char_id", session.selectedCharacterID,
|
||||
"reason", "equipment_slot26_absent")
|
||||
return nil
|
||||
}
|
||||
creature := currentEquippedCreatureFromEquipment(equipped)
|
||||
if !creature.valid() || int64(creature.itemID) != equipped.ItemID {
|
||||
return fmt.Errorf(
|
||||
"equipped creature item refresh rejected invalid slot 26: item=%d serial=%d",
|
||||
equipped.ItemID,
|
||||
creature.serialOrHandle,
|
||||
)
|
||||
}
|
||||
entry := currentEquippedCreatureItemRefreshEntry(equipped)
|
||||
if binary.LittleEndian.Uint16(entry.data[0:2]) != 26 {
|
||||
return fmt.Errorf("equipped creature item refresh could not project slot 26")
|
||||
}
|
||||
body := buildCurrentItemUpdateBody(currentSocketListEquipment, []currentItemListEntry{entry})
|
||||
s.logPacketEvent("game-upper-current-equipped-creature-item-refresh-send",
|
||||
"conn_id", session.connID,
|
||||
"channel_id", session.channel.ID,
|
||||
"source", source,
|
||||
"char_id", session.selectedCharacterID,
|
||||
"msg_id", uint16(dnfenum.CmdPacketWalkoutPartyMember),
|
||||
"classification", 0,
|
||||
"list_type", currentSocketListEquipment,
|
||||
"slot", 26,
|
||||
"item_id", creature.itemID,
|
||||
"creature_serial", creature.serialOrHandle,
|
||||
"enchant_card_id", binary.LittleEndian.Uint32(entry.data[0x0E:0x12]),
|
||||
"enchant_upgrade_count", entry.data[0x12],
|
||||
"entry_count", 1,
|
||||
"body_len", len(body),
|
||||
"sequence", "existing_slot26_object_then_pet_only_op14_dynamic_state_refresh")
|
||||
return s.sendGameUpperRawClass(
|
||||
session,
|
||||
uint16(dnfenum.CmdPacketWalkoutPartyMember),
|
||||
body,
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
// currentEquippedCreatureItemRefreshEntry must not use the legacy equipment
|
||||
// raw row as a 0x77-byte base. The legacy card begins at +16 and its serial is
|
||||
// repeated at +24; copying those bytes verbatim makes the card's third byte an
|
||||
// enchant upgrade and serial<<16 a fake remaining duration (serial 41 appears
|
||||
// as roughly 32 days). Build the list-3 row from zero and map only fields with
|
||||
// proved equivalents in the current item-update grammar.
|
||||
func currentEquippedCreatureItemRefreshEntry(equipped dnfrepo.EquipmentEntry) currentItemListEntry {
|
||||
var entry currentItemListEntry
|
||||
entry.patchCore(26, sceneInventoryUint32FromInt64(equipped.ItemID), currentItemListEquipmentInstance(equipped))
|
||||
entry.data[0x0A] = currentItemListEquipmentExtData(equipped)
|
||||
binary.LittleEndian.PutUint16(entry.data[0x0B:0x0D], currentItemListEquipmentDurability(equipped))
|
||||
entry.data[0x0D] = currentItemListBindFlag(equipped.Bind, equipped.Extra)
|
||||
binary.LittleEndian.PutUint32(entry.data[0x0E:0x12], currentEquippedCreatureEnchantCardID(equipped))
|
||||
entry.data[0x12] = currentEquippedCreatureEnchantUpgrade(equipped)
|
||||
entry.data[0x13] = currentEquippedCreatureEnchantAmplifyType(equipped)
|
||||
binary.LittleEndian.PutUint16(entry.data[0x14:0x16], currentEquippedCreatureEnchantAmplifyValue(equipped))
|
||||
expire := currentItemListEquipmentExpire(equipped)
|
||||
binary.LittleEndian.PutUint32(
|
||||
entry.data[currentPetRemainSecondsOffset:currentPetRemainSecondsOffset+4],
|
||||
currentPetRemainingSecondsAt(expire, time.Now().UTC()),
|
||||
)
|
||||
binary.LittleEndian.PutUint32(
|
||||
entry.data[currentItemListExpireTimeOffset:currentItemListExpireTimeOffset+4],
|
||||
expire,
|
||||
)
|
||||
entry.data[0x57] = sceneInventoryExtraByte(equipped.Extra, "byte_57", "value_57")
|
||||
entry.data[0x58] = sceneInventoryExtraByte(equipped.Extra, "byte_58", "value_58")
|
||||
entry.data[0x59] = sceneInventoryExtraByte(equipped.Extra, "byte_59", "value_59")
|
||||
entry.copyFixed(0x72, currentItemListFixedExtraBytes(equipped.Extra, 5, "tail_data_72", "tail72"))
|
||||
return entry
|
||||
}
|
||||
|
||||
func currentEquippedCreatureEnchantCardID(equipped dnfrepo.EquipmentEntry) uint32 {
|
||||
if value, ok := currentPetEnchantExtraUint(equipped.Extra, "pet_enchant_card_item_id", "enchant_card_id", "value_a"); ok {
|
||||
return sceneInventoryClampUint32(value)
|
||||
}
|
||||
if currentEquipmentEntryHasCurrentRaw77(equipped) {
|
||||
return binary.LittleEndian.Uint32(equipped.RawEntry[0x0E:0x12])
|
||||
}
|
||||
if len(equipped.RawEntry) >= 20 {
|
||||
return binary.LittleEndian.Uint32(equipped.RawEntry[16:20])
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func mergeCurrentPetEnchantFromRecord(equipped *dnfrepo.EquipmentEntry, record dnfrepo.PetRecord) {
|
||||
if equipped == nil || equipped.SlotIndex != 26 || equipped.ItemID <= 0 {
|
||||
return
|
||||
}
|
||||
creature := currentEquippedCreatureFromEquipment(*equipped)
|
||||
if !creature.valid() {
|
||||
return
|
||||
}
|
||||
if currentEquippedCreatureEnchantCardID(*equipped) != 0 {
|
||||
return
|
||||
}
|
||||
for _, pet := range record.Entries {
|
||||
petSerial := pet.CreatureKey
|
||||
if petSerial == 0 {
|
||||
if parsed, err := strconv.ParseUint(strings.TrimSpace(pet.PetKey), 0, 32); err == nil {
|
||||
petSerial = uint32(parsed)
|
||||
}
|
||||
}
|
||||
if pet.ItemID != equipped.ItemID || petSerial != creature.serialOrHandle {
|
||||
continue
|
||||
}
|
||||
card, ok := currentPetEnchantExtraUint(pet.Extra, "pet_enchant_card_item_id", "enchant_card_id", "value_a")
|
||||
if !ok || card == 0 {
|
||||
return
|
||||
}
|
||||
upgrade := uint64(0)
|
||||
if value, found := currentPetEnchantExtraUint(pet.Extra, "pet_enchant_upgrade_count", "enchant_upgrade_count", "byte_12"); found {
|
||||
upgrade = value
|
||||
}
|
||||
if equipped.Extra == nil {
|
||||
equipped.Extra = make(map[string]string, 8)
|
||||
}
|
||||
cardValue := strconv.FormatUint(card, 10)
|
||||
upgradeValue := strconv.FormatUint(upgrade, 10)
|
||||
equipped.Extra["pet_enchant_card_item_id"] = cardValue
|
||||
equipped.Extra["enchant_card_id"] = cardValue
|
||||
equipped.Extra["value_a"] = cardValue
|
||||
equipped.Extra["pet_enchant_upgrade_count"] = upgradeValue
|
||||
equipped.Extra["enchant_upgrade_count"] = upgradeValue
|
||||
equipped.Extra["byte_12"] = upgradeValue
|
||||
if len(equipped.RawEntry) >= 0x15 {
|
||||
equipped.RawEntry = append([]byte(nil), equipped.RawEntry...)
|
||||
binary.LittleEndian.PutUint32(equipped.RawEntry[0x10:0x14], uint32(card))
|
||||
equipped.RawEntry[0x14] = byte(min(upgrade, 0xFF))
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func currentPetEnchantExtraUint(extra map[string]string, keys ...string) (uint64, bool) {
|
||||
var first uint64
|
||||
found := false
|
||||
for _, key := range keys {
|
||||
value, ok := sceneInventoryExtraUint(extra, key)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if !found {
|
||||
first, found = value, true
|
||||
}
|
||||
if value > 0 {
|
||||
return value, true
|
||||
}
|
||||
}
|
||||
return first, found
|
||||
}
|
||||
|
||||
func currentEquippedCreatureEnchantUpgrade(equipped dnfrepo.EquipmentEntry) byte {
|
||||
if value, ok := sceneInventoryExtraUint(
|
||||
equipped.Extra,
|
||||
"pet_enchant_upgrade_count",
|
||||
"enchant_upgrade_count",
|
||||
"byte_12",
|
||||
"value_12",
|
||||
); ok {
|
||||
return byte(min(value, 0xFF))
|
||||
}
|
||||
if currentEquipmentEntryHasCurrentRaw77(equipped) {
|
||||
return equipped.RawEntry[0x12]
|
||||
}
|
||||
if len(equipped.RawEntry) > 20 {
|
||||
return equipped.RawEntry[20]
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func currentEquippedCreatureEnchantAmplifyType(equipped dnfrepo.EquipmentEntry) byte {
|
||||
if value, ok := sceneInventoryExtraUint(equipped.Extra, "byte_13", "value_13", "value_c"); ok {
|
||||
return byte(min(value, 0xFF))
|
||||
}
|
||||
if currentEquipmentEntryHasCurrentRaw77(equipped) {
|
||||
return equipped.RawEntry[0x13]
|
||||
}
|
||||
if len(equipped.RawEntry) > 21 {
|
||||
return equipped.RawEntry[21]
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func currentEquippedCreatureEnchantAmplifyValue(equipped dnfrepo.EquipmentEntry) uint16 {
|
||||
if value, ok := sceneInventoryExtraUint(equipped.Extra, "marker_16", "marker16", "value_d"); ok {
|
||||
return uint16(min(value, 0xFFFF))
|
||||
}
|
||||
if currentEquipmentEntryHasCurrentRaw77(equipped) {
|
||||
return binary.LittleEndian.Uint16(equipped.RawEntry[0x14:0x16])
|
||||
}
|
||||
if len(equipped.RawEntry) >= 24 {
|
||||
return binary.LittleEndian.Uint16(equipped.RawEntry[22:24])
|
||||
}
|
||||
return 0
|
||||
}
|
||||
Reference in new issue
Block a user