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,219 @@
|
||||
package inventory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"longheng.io/server/internal/modules/dnf/alignedcmd"
|
||||
dnfrepo "longheng.io/server/internal/modules/dnf/repository"
|
||||
)
|
||||
|
||||
const equipmentRegenerationErrorDefault byte = 17
|
||||
|
||||
type EquipmentRegenerationChangedRow struct {
|
||||
SlotIndex int16
|
||||
Stack dnfrepo.ItemStack
|
||||
}
|
||||
|
||||
type EquipmentRegenerationResult struct {
|
||||
Success bool
|
||||
ErrorCode byte
|
||||
SourceSlotIndex int16
|
||||
SourceItemID int64
|
||||
ResultItemID int64
|
||||
ResultStack dnfrepo.ItemStack
|
||||
ChangedRows []EquipmentRegenerationChangedRow
|
||||
CandidateCount int
|
||||
Changed bool
|
||||
}
|
||||
|
||||
func (o *Owner) RegenerateEquipment(ctx context.Context, accountID string, characterID uint16, request EquipmentRegenerationRequest, resolver alignedcmd.EquipmentRegenerationResolver) (EquipmentRegenerationResult, error) {
|
||||
base := EquipmentRegenerationResult{ErrorCode: equipmentRegenerationErrorDefault, SourceSlotIndex: request.SourceSlotIndex}
|
||||
if o == nil || o.repo == nil || o.accountRepo == nil {
|
||||
return base, ErrOwnerUnavailable
|
||||
}
|
||||
if characterID == 0 {
|
||||
return base, ErrCharacterRequired
|
||||
}
|
||||
accountID = strings.TrimSpace(accountID)
|
||||
if accountID == "" {
|
||||
return base, ErrAccountRequired
|
||||
}
|
||||
if resolver == nil {
|
||||
return base, fmt.Errorf("equipment regeneration runtime-PVF resolver required")
|
||||
}
|
||||
if request.SourceSlotIndex < 9 || request.Mode > 1 {
|
||||
return base, nil
|
||||
}
|
||||
|
||||
characterKey := strconv.FormatUint(uint64(characterID), 10)
|
||||
if !o.inItemTransaction {
|
||||
var result EquipmentRegenerationResult
|
||||
err := o.withinAccountCharacterAssetTransaction(ctx, accountID, characterKey, func(txOwner *Owner) error {
|
||||
var innerErr error
|
||||
result, innerErr = txOwner.RegenerateEquipment(ctx, accountID, characterID, request, resolver)
|
||||
return innerErr
|
||||
})
|
||||
if err != nil {
|
||||
return base, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
inventoryRecord, found, err := o.repo.Load(ctx, characterKey)
|
||||
if err != nil {
|
||||
return base, err
|
||||
}
|
||||
if !found {
|
||||
return base, ErrInventoryNotFound
|
||||
}
|
||||
inventoryRecord = dnfrepo.CloneInventory(inventoryRecord)
|
||||
if inventoryRecord.Slots == nil {
|
||||
inventoryRecord.Slots = make(map[string]dnfrepo.ItemStack)
|
||||
}
|
||||
sourceKey := slotKey(listTypeMain, request.SourceSlotIndex)
|
||||
source, found := inventoryRecord.Slots[sourceKey]
|
||||
if !found || source.ItemID <= 0 || source.Count != 1 || isEquipmentLocked(source) || !isUpgradeableEquipmentStack(source) {
|
||||
return base, nil
|
||||
}
|
||||
base.SourceItemID = source.ItemID
|
||||
|
||||
resolution, err := resolver(source.ItemID, request.Mode, request.Part)
|
||||
if err != nil {
|
||||
return base, err
|
||||
}
|
||||
if !resolution.Eligible || resolution.SourceItemID != source.ItemID || resolution.ResultItemID <= 0 ||
|
||||
strings.TrimSpace(resolution.SourcePVFPath) == "" || strings.TrimSpace(resolution.ResultPVFPath) == "" ||
|
||||
strings.TrimSpace(resolution.ResultEquipmentType) == "" || resolution.QualitySeed == 0 || len(resolution.Materials) == 0 {
|
||||
return base, nil
|
||||
}
|
||||
|
||||
accountRecord, _, err := o.accountRepo.Load(ctx, accountID)
|
||||
if err != nil {
|
||||
return base, err
|
||||
}
|
||||
accountRecord = dnfrepo.CloneAccountInventory(accountRecord)
|
||||
accountRecord.AccountID = accountID
|
||||
if accountRecord.Slots == nil {
|
||||
accountRecord.Slots = make(map[string]dnfrepo.ItemStack)
|
||||
}
|
||||
|
||||
changed := make(map[int16]dnfrepo.ItemStack)
|
||||
for _, requirement := range resolution.Materials {
|
||||
if requirement.ItemID <= 0 || requirement.Count <= 0 {
|
||||
return base, nil
|
||||
}
|
||||
remaining := requirement.Count
|
||||
if requirement.AccountShared {
|
||||
slot, ok := upgradeAccountSharedMaterialSlot(requirement.ItemID)
|
||||
if !ok {
|
||||
return base, nil
|
||||
}
|
||||
key := slotKey(listTypeMain, slot)
|
||||
stack, ok := accountRecord.Slots[key]
|
||||
if !ok || stack.ItemID != requirement.ItemID || stack.Count < remaining {
|
||||
return base, nil
|
||||
}
|
||||
stack = cloneStack(stack)
|
||||
stack.Count -= remaining
|
||||
if stack.Count == 0 {
|
||||
delete(accountRecord.Slots, key)
|
||||
stack = dnfrepo.ItemStack{}
|
||||
} else {
|
||||
updateStackRawAmount(&stack)
|
||||
accountRecord.Slots[key] = stack
|
||||
}
|
||||
changed[slot] = stack
|
||||
continue
|
||||
}
|
||||
|
||||
slots := make([]int, 0)
|
||||
for key, stack := range inventoryRecord.Slots {
|
||||
listType, slot, ok := parseSlotKey(key)
|
||||
if ok && listType == listTypeMain && slot != request.SourceSlotIndex && stack.ItemID == requirement.ItemID && stack.Count > 0 {
|
||||
slots = append(slots, int(slot))
|
||||
}
|
||||
}
|
||||
sort.Ints(slots)
|
||||
for _, rawSlot := range slots {
|
||||
if remaining == 0 {
|
||||
break
|
||||
}
|
||||
slot := int16(rawSlot)
|
||||
key := slotKey(listTypeMain, slot)
|
||||
stack := cloneStack(inventoryRecord.Slots[key])
|
||||
take := remaining
|
||||
if stack.Count < take {
|
||||
take = stack.Count
|
||||
}
|
||||
stack.Count -= take
|
||||
remaining -= take
|
||||
if stack.Count == 0 {
|
||||
delete(inventoryRecord.Slots, key)
|
||||
stack = dnfrepo.ItemStack{}
|
||||
} else {
|
||||
updateStackRawAmount(&stack)
|
||||
inventoryRecord.Slots[key] = stack
|
||||
}
|
||||
changed[slot] = stack
|
||||
}
|
||||
if remaining != 0 {
|
||||
return base, nil
|
||||
}
|
||||
}
|
||||
|
||||
sealFlag := firstExtraInt(source.Extra, boolInt64(source.Bind), "seal_flag", "seal", "bind_flag", "bind")
|
||||
quality := strconv.FormatUint(uint64(resolution.QualitySeed), 10)
|
||||
durability := strconv.FormatUint(uint64(resolution.ResultDurability), 10)
|
||||
resultStack := dnfrepo.ItemStack{
|
||||
ItemID: resolution.ResultItemID,
|
||||
Count: 1,
|
||||
Bind: source.Bind,
|
||||
Extra: map[string]string{
|
||||
"item_kind": "equipment",
|
||||
"pvf_path": resolution.ResultPVFPath,
|
||||
"equipment_type": resolution.ResultEquipmentType,
|
||||
"rarity": strconv.FormatInt(resolution.ResultRarity, 10),
|
||||
"minimum_level": strconv.FormatInt(resolution.ResultMinimumLevel, 10),
|
||||
"durability": durability,
|
||||
"max_durability": durability,
|
||||
"seal_flag": strconv.FormatInt(sealFlag, 10),
|
||||
"quality_seed": quality,
|
||||
"amount_or_count": quality,
|
||||
"source": "equipment_regeneration_op407",
|
||||
},
|
||||
}
|
||||
resultStack.RawEntry = currentRawEntryForStack(request.SourceSlotIndex, resultStack)
|
||||
inventoryRecord.Slots[sourceKey] = resultStack
|
||||
changed[request.SourceSlotIndex] = resultStack
|
||||
|
||||
now := time.Now()
|
||||
inventoryRecord.UpdatedAt = now
|
||||
accountRecord.UpdatedAt = now
|
||||
if err := o.accountRepo.Save(ctx, accountRecord); err != nil {
|
||||
return base, err
|
||||
}
|
||||
if err := dnfrepo.SaveInventoryFields(ctx, o.repo, inventoryRecord, dnfrepo.InventoryFieldSlots); err != nil {
|
||||
return base, err
|
||||
}
|
||||
|
||||
rows := make([]EquipmentRegenerationChangedRow, 0, len(changed))
|
||||
for slot, stack := range changed {
|
||||
rows = append(rows, EquipmentRegenerationChangedRow{SlotIndex: slot, Stack: cloneStack(stack)})
|
||||
}
|
||||
sort.Slice(rows, func(i, j int) bool { return rows[i].SlotIndex < rows[j].SlotIndex })
|
||||
return EquipmentRegenerationResult{
|
||||
Success: true,
|
||||
SourceSlotIndex: request.SourceSlotIndex,
|
||||
SourceItemID: source.ItemID,
|
||||
ResultItemID: resolution.ResultItemID,
|
||||
ResultStack: cloneStack(resultStack),
|
||||
ChangedRows: rows,
|
||||
CandidateCount: resolution.CandidateCount,
|
||||
Changed: true,
|
||||
}, nil
|
||||
}
|
||||
Reference in new issue
Block a user