Files
90CNServer/server/internal/services/dnfbridge/dungeon_card_reward_plan.go
T
hanfuit 645b64fe21 DNF90 服务端与启动器完整源码(含 GM 工具功能增强)
服务端(Go):
- GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面
- GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新
- 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新)
- 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍
- 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0)
- 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算

启动器(Python/JS):
- 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭
- GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐)
- 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
2026-11-04 14:31:55 +08:00

124 lines
4.0 KiB
Go

package dnfbridge
import (
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"fmt"
"hash"
"sort"
"strings"
)
// newDungeonCardRewardPlan freezes already-resolved rewards for one run. It
// does not choose item ids, rarity, gold values, or PVF paths; callers must
// supply values resolved from the runtime catalogs and real run state.
func newDungeonCardRewardPlan(
identity dungeonCardPlanIdentity,
source string,
free dungeonCardRewardBundle,
paid dungeonCardRewardBundle,
) (dungeonCardRewardPlan, error) {
return newDungeonCardRewardPlanWithBlackDiamond(
identity,
source,
free,
paid,
[dungeonCardSlotsPerSide]dungeonCardRewardBundle{},
)
}
func newDungeonCardRewardPlanWithBlackDiamond(
identity dungeonCardPlanIdentity,
source string,
free dungeonCardRewardBundle,
paid dungeonCardRewardBundle,
blackDiamond [dungeonCardSlotsPerSide]dungeonCardRewardBundle,
) (dungeonCardRewardPlan, error) {
identity.CharacterID = strings.TrimSpace(identity.CharacterID)
source = strings.TrimSpace(source)
if identity.CharacterID == "" || identity.DungeonID <= 0 || identity.MazeIndex < 0 || source == "" {
return dungeonCardRewardPlan{}, errDungeonCardPlanInvalid
}
var numeric [16]byte
binary.LittleEndian.PutUint64(numeric[0:8], uint64(identity.DungeonID))
binary.LittleEndian.PutUint32(numeric[8:12], uint32(identity.MazeIndex))
binary.LittleEndian.PutUint32(numeric[12:16], identity.RunSeed)
hash := sha256.New()
_, _ = hash.Write([]byte("dnf-card-plan-v3\x00"))
_, _ = hash.Write([]byte(identity.CharacterID))
_, _ = hash.Write([]byte{0})
_, _ = hash.Write(numeric[:])
_, _ = hash.Write([]byte(source))
writeDungeonCardBundleIdentity(hash, free)
writeDungeonCardBundleIdentity(hash, paid)
for member := range blackDiamond {
writeDungeonCardBundleIdentity(hash, blackDiamond[member])
}
id := hex.EncodeToString(hash.Sum(nil)[:16])
plan := dungeonCardRewardPlan{
ID: id,
CharacterID: identity.CharacterID,
Source: source,
Sides: [dungeonCardSideCount]dungeonCardRewardBundle{
cloneDungeonCardRewardBundle(free),
cloneDungeonCardRewardBundle(paid),
},
}
for member := range blackDiamond {
plan.BlackDiamond[member] = cloneDungeonCardRewardBundle(blackDiamond[member])
}
if err := validateDungeonCardPlan(plan); err != nil {
return dungeonCardRewardPlan{}, fmt.Errorf("freeze dungeon card plan: %w", err)
}
return plan, nil
}
func writeDungeonCardBundleIdentity(digest hash.Hash, bundle dungeonCardRewardBundle) {
var numeric [8]byte
binary.LittleEndian.PutUint64(numeric[:], uint64(bundle.Gold))
_, _ = digest.Write(numeric[:])
binary.LittleEndian.PutUint64(numeric[:], uint64(len(bundle.Items)))
_, _ = digest.Write(numeric[:])
for _, item := range bundle.Items {
binary.LittleEndian.PutUint64(numeric[:], uint64(item.ItemID))
_, _ = digest.Write(numeric[:])
binary.LittleEndian.PutUint64(numeric[:], uint64(item.Count))
_, _ = digest.Write(numeric[:])
flags := byte(0)
if item.Stackable {
flags |= 1
}
if item.Bind {
flags |= 2
}
_, _ = digest.Write([]byte{flags})
binary.LittleEndian.PutUint16(numeric[0:2], uint16(item.SlotStart))
binary.LittleEndian.PutUint16(numeric[2:4], uint16(item.SlotEnd))
_, _ = digest.Write(numeric[0:4])
binary.LittleEndian.PutUint64(numeric[:], uint64(item.ExpireAt.Unix()))
_, _ = digest.Write(numeric[:])
binary.LittleEndian.PutUint64(numeric[:], uint64(len(item.RawEntry)))
_, _ = digest.Write(numeric[:])
_, _ = digest.Write(item.RawEntry)
keys := make([]string, 0, len(item.Extra))
for key := range item.Extra {
keys = append(keys, key)
}
sort.Strings(keys)
binary.LittleEndian.PutUint64(numeric[:], uint64(len(keys)))
_, _ = digest.Write(numeric[:])
for _, key := range keys {
value := item.Extra[key]
binary.LittleEndian.PutUint64(numeric[:], uint64(len(key)))
_, _ = digest.Write(numeric[:])
_, _ = digest.Write([]byte(key))
binary.LittleEndian.PutUint64(numeric[:], uint64(len(value)))
_, _ = digest.Write(numeric[:])
_, _ = digest.Write([]byte(value))
}
}
}