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,474 @@
|
||||
package dnfbridge
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
dnfpvf "longheng.io/server/internal/modules/dnf/pvf"
|
||||
)
|
||||
|
||||
// 「城镇增益药剂」(史诗Buff药剂一族)的 buff 数据表。
|
||||
//
|
||||
// 2026-10-03 现场(服主报,测试x = char 11):史诗Buff药剂 490002458 修好"能用"之后
|
||||
// 仍然看不到 buff 图标。翻 PVF 找到两张表,这一族的效果、图标、概率**全在这里**,
|
||||
// 不在 .stk 自己的文档里:
|
||||
//
|
||||
// etc/variousbuffstackablelist.etc 道具 → buff + 概率
|
||||
// [index] 490002458
|
||||
// [probability buff list] 1136 100 ← buff 1136,权重 100
|
||||
// [/index]
|
||||
// [index] 10008219
|
||||
// [probability buff list] 1090 60 1091 30 1092 10 ← 三档随机
|
||||
// [/index]
|
||||
//
|
||||
// etc/variousbufflist.etc buff → 图标 / 说明 / 效果 / 数值
|
||||
// [index] 1136
|
||||
// [system group] `random_box_buff`
|
||||
// [icon index] 183 ← 客户端画图标用的就是这个
|
||||
// [tooltip text] `史诗道具掉落率提升`
|
||||
// [effect] [name] `epic_rarity_item_drop_rate` [param] 100 [/param] [/effect]
|
||||
// [overlap] 1
|
||||
// [apply type] [type] `expire_time` [param] 10 0 [/param] [/apply type]
|
||||
// [/index]
|
||||
//
|
||||
// 于是三件事都有权威依据了:
|
||||
// 1. **数值**:`epic_rarity_item_drop_rate` 的 param 就是"增加百分之多少"。
|
||||
// 1136=100(2 倍)、1090/1091/1092=100/150/200(赛丽亚三档)、1239=10、1240=5。
|
||||
// 以前按 `[explain]` 文字统一取 200 只对史诗Buff药剂是对的,小灯笼大瓶/小瓶其实是
|
||||
// +10%/+5%,所以现在改成读这张表;表里查不到的道具(例如 490000413 这种老活动券)
|
||||
// 才回退到说明文字。
|
||||
// 2. **概率**:`[probability buff list]` 是"按权重随机给一个 buff",所以**服务端**掷点,
|
||||
// 客户端不可能自己算 —— 这就是"必须下发状态"的原因。
|
||||
// 3. **图标**:`[icon index]` + `[tooltip text]` 由客户端按 buff id 去这张表里取,
|
||||
// 服务端只要把"你拿到了哪个 buff、还剩多久"告诉它即可(见
|
||||
// stat_change_potion_projection.go 的 class0/op174)。
|
||||
const (
|
||||
currentVariousBuffStackableList = "etc/variousbuffstackablelist.etc"
|
||||
currentVariousBuffList = "etc/variousbufflist.etc"
|
||||
// currentEpicRarityDropRateEffect 是掉落率效果的 name;param 是"增加百分比"。
|
||||
currentEpicRarityDropRateEffect = "epic_rarity_item_drop_rate"
|
||||
// currentUpgradeSuccessRateEffect 是**强化/增幅成功率**效果的 name;param 是
|
||||
// "成功率增加多少个百分点"。现场抄录(2026-10-03,cmd/scratch/pvfmember 可复核):
|
||||
//
|
||||
// 1097 param 10 `强化成功率提升 (下级)`
|
||||
// 1098 param 20 `强化成功率提升 (中级)`
|
||||
// 1099 param 30 `强化成功率提升 (上级)`
|
||||
// 1180..1187 param 1..8(活动 SpecUp_Event_Buff4)
|
||||
//
|
||||
// 道具→buff 的掷点在同一份 etc/variousbuffstackablelist.etc 里:
|
||||
// 赛丽亚的幸运强化药剂 10008220 → `1097 60 1098 30 1099 10`。
|
||||
currentUpgradeSuccessRateEffect = "item_usual_upgrade_success_rate"
|
||||
// currentDungeonClearExpRateEffect 是"地下城通关经验值 +N%"(赛丽亚的幸运经验药剂
|
||||
// 10008222 → buff 1103/1104/1105 概率 30/50/20,param 50/100/200)。
|
||||
// 通关经验是服务端发的,所以这一族必须服务端算。
|
||||
currentDungeonClearExpRateEffect = "dungeon_clear_exp_rate"
|
||||
)
|
||||
|
||||
// currentVariousBuffRoll 是"道具 → buff"里的一项(buff id + 权重)。
|
||||
type currentVariousBuffRoll struct {
|
||||
BuffID int64
|
||||
Weight int64
|
||||
// BonusPercent 只有 clientBuffRollsForItem 会填(厂商那几族把 param 一起带出来,
|
||||
// 客户端自己施加效果时用不到它,服务端只用于日志/排查)。解析器不填,保持 0。
|
||||
BonusPercent int64
|
||||
}
|
||||
|
||||
// currentVariousBuffEffect 是 buff 定义表里我们关心的字段。
|
||||
type currentVariousBuffEffect struct {
|
||||
BuffID int64
|
||||
SystemGroup string
|
||||
IconIndex int64
|
||||
Tooltip string
|
||||
// EffectName / EffectParams 来自 [effect] 段。掉率增益用 params[0] 表示 +N%。
|
||||
EffectName string
|
||||
EffectParams []int64
|
||||
// ApplyType 来自 [apply type] 的 [type](`expire_time` / `dungeon_one_match` ...)。
|
||||
ApplyType string
|
||||
}
|
||||
|
||||
// currentVariousBuffTables 是两张表解析后的结果。
|
||||
type currentVariousBuffTables struct {
|
||||
// ByItem 道具 id → 候选 buff(按权重)。
|
||||
ByItem map[uint32][]currentVariousBuffRoll
|
||||
// ByBuff buff id → 定义。
|
||||
ByBuff map[int64]currentVariousBuffEffect
|
||||
}
|
||||
|
||||
// loadCurrentVariousBuffTables 读两张表。任一读不到就返回错误,由调用方决定是降级
|
||||
// (回退到 `[explain]` 文字)还是让整条路径失败 —— 这里不吞错。
|
||||
func loadCurrentVariousBuffTables(source dnfpvf.Source) (currentVariousBuffTables, error) {
|
||||
tables := currentVariousBuffTables{
|
||||
ByItem: make(map[uint32][]currentVariousBuffRoll),
|
||||
ByBuff: make(map[int64]currentVariousBuffEffect),
|
||||
}
|
||||
stackableText, err := source.ReadText(currentVariousBuffStackableList)
|
||||
if err != nil {
|
||||
return tables, fmt.Errorf("read %s: %w", currentVariousBuffStackableList, err)
|
||||
}
|
||||
stackableDoc, err := dnfpvf.Parse(currentVariousBuffStackableList, stackableText)
|
||||
if err != nil {
|
||||
return tables, fmt.Errorf("parse %s: %w", currentVariousBuffStackableList, err)
|
||||
}
|
||||
buffText, err := source.ReadText(currentVariousBuffList)
|
||||
if err != nil {
|
||||
return tables, fmt.Errorf("read %s: %w", currentVariousBuffList, err)
|
||||
}
|
||||
buffDoc, err := dnfpvf.Parse(currentVariousBuffList, buffText)
|
||||
if err != nil {
|
||||
return tables, fmt.Errorf("parse %s: %w", currentVariousBuffList, err)
|
||||
}
|
||||
tables.ByItem = parseCurrentVariousBuffStackables(stackableDoc)
|
||||
tables.ByBuff = parseCurrentVariousBuffEffects(buffDoc)
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
// indexBlocks 把 token 流按 `[index] ... [/index]` 切段,返回每段的 token 窗口。
|
||||
func indexBlocks(document *dnfpvf.Document) [][]dnfpvf.Token {
|
||||
if document == nil {
|
||||
return nil
|
||||
}
|
||||
tokens := document.Tokens
|
||||
blocks := make([][]dnfpvf.Token, 0, 64)
|
||||
for i := 0; i < len(tokens); i++ {
|
||||
if !isPVFSectionToken(tokens[i], "index") {
|
||||
continue
|
||||
}
|
||||
for j := i + 1; j < len(tokens); j++ {
|
||||
if isPVFSectionToken(tokens[j], "/index") {
|
||||
blocks = append(blocks, tokens[i+1:j])
|
||||
i = j
|
||||
break
|
||||
}
|
||||
if isPVFSectionToken(tokens[j], "index") {
|
||||
i = j - 1
|
||||
break
|
||||
}
|
||||
if j == len(tokens)-1 {
|
||||
blocks = append(blocks, tokens[i+1:])
|
||||
i = j
|
||||
}
|
||||
}
|
||||
}
|
||||
return blocks
|
||||
}
|
||||
|
||||
// parseCurrentVariousBuffStackables 解 [index] <道具> ([buff list]|[probability buff list]) ...
|
||||
//
|
||||
// 两种段名都要认:
|
||||
// - `[buff list]` —— 确定性,一个 buff id
|
||||
// - `[probability buff list]` —— (buff id, 权重) 成对出现,服务端按权重掷点
|
||||
func parseCurrentVariousBuffStackables(document *dnfpvf.Document) map[uint32][]currentVariousBuffRoll {
|
||||
out := make(map[uint32][]currentVariousBuffRoll)
|
||||
for _, block := range indexBlocks(document) {
|
||||
itemID := int64(0)
|
||||
rolls := make([]currentVariousBuffRoll, 0, 2)
|
||||
section := ""
|
||||
ints := make([]int64, 0, 6)
|
||||
flush := func() {
|
||||
switch section {
|
||||
case "buff list":
|
||||
for _, value := range ints {
|
||||
rolls = append(rolls, currentVariousBuffRoll{BuffID: value, Weight: 100})
|
||||
}
|
||||
case "probability buff list":
|
||||
if len(ints)%2 == 0 {
|
||||
for k := 0; k+1 < len(ints); k += 2 {
|
||||
rolls = append(rolls, currentVariousBuffRoll{BuffID: ints[k], Weight: ints[k+1]})
|
||||
}
|
||||
} else {
|
||||
// 退化写法:只有 buff id,没有权重 ⇒ 每项等权。
|
||||
for _, value := range ints {
|
||||
rolls = append(rolls, currentVariousBuffRoll{BuffID: value, Weight: 100})
|
||||
}
|
||||
}
|
||||
}
|
||||
ints = ints[:0]
|
||||
}
|
||||
for _, token := range block {
|
||||
if token.Kind == dnfpvf.TokenSection {
|
||||
flush()
|
||||
section = strings.ToLower(strings.TrimSpace(token.Value))
|
||||
continue
|
||||
}
|
||||
if token.Kind != dnfpvf.TokenInt {
|
||||
continue
|
||||
}
|
||||
if itemID == 0 && section == "" {
|
||||
itemID = token.Int
|
||||
continue
|
||||
}
|
||||
if section == "buff list" || section == "probability buff list" {
|
||||
ints = append(ints, token.Int)
|
||||
}
|
||||
}
|
||||
flush()
|
||||
if itemID <= 0 || itemID > math.MaxUint32 || len(rolls) == 0 {
|
||||
continue
|
||||
}
|
||||
out[uint32(itemID)] = rolls
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// parseCurrentVariousBuffEffects 解 [index] <buff id> 下的图标/说明/效果/数值。
|
||||
func parseCurrentVariousBuffEffects(document *dnfpvf.Document) map[int64]currentVariousBuffEffect {
|
||||
out := make(map[int64]currentVariousBuffEffect)
|
||||
for _, block := range indexBlocks(document) {
|
||||
effect := currentVariousBuffEffect{}
|
||||
section := ""
|
||||
expectName := false
|
||||
inEffect := false
|
||||
inParam := false
|
||||
for _, token := range block {
|
||||
if token.Kind == dnfpvf.TokenSection {
|
||||
name := strings.ToLower(strings.TrimSpace(token.Value))
|
||||
section = name
|
||||
inParam = name == "param"
|
||||
if name == "effect" {
|
||||
inEffect = true
|
||||
}
|
||||
if name == "/effect" {
|
||||
inEffect = false
|
||||
}
|
||||
if name == "name" {
|
||||
expectName = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case effect.BuffID == 0 && token.Kind == dnfpvf.TokenInt:
|
||||
effect.BuffID = token.Int
|
||||
case section == "system group" && token.Kind == dnfpvf.TokenString:
|
||||
effect.SystemGroup = cleanCurrentPVFText(token.Value)
|
||||
case section == "icon index" && token.Kind == dnfpvf.TokenInt:
|
||||
effect.IconIndex = token.Int
|
||||
case section == "tooltip text" && token.Kind == dnfpvf.TokenString:
|
||||
effect.Tooltip = cleanCurrentPVFText(token.Value)
|
||||
case expectName && token.Kind == dnfpvf.TokenString:
|
||||
effect.EffectName = cleanCurrentPVFText(token.Value)
|
||||
expectName = false
|
||||
case inEffect && inParam && token.Kind == dnfpvf.TokenInt:
|
||||
effect.EffectParams = append(effect.EffectParams, token.Int)
|
||||
case section == "apply type" && token.Kind == dnfpvf.TokenString && effect.ApplyType == "":
|
||||
// [apply type] 段里第一个字符串就是 [type] 的值(`expire_time` 等)。
|
||||
value := cleanCurrentPVFText(token.Value)
|
||||
if !strings.EqualFold(value, "type") {
|
||||
effect.ApplyType = value
|
||||
}
|
||||
}
|
||||
}
|
||||
if effect.BuffID <= 0 {
|
||||
continue
|
||||
}
|
||||
out[effect.BuffID] = effect
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func cleanCurrentPVFText(value string) string {
|
||||
return strings.TrimSpace(strings.Trim(value, "`"))
|
||||
}
|
||||
|
||||
// dispatchBonusPercent 把 buff 的效果翻译成"数值":只有当效果名就是 wantEffect 时才算数,
|
||||
// param 缺省或非正数按"不认"处理(= 服务端不改任何东西),绝不猜。
|
||||
func (effect currentVariousBuffEffect) dispatchBonusPercent(wantEffect string) (int64, bool) {
|
||||
if !strings.EqualFold(strings.TrimSpace(effect.EffectName), wantEffect) {
|
||||
return 0, false
|
||||
}
|
||||
if len(effect.EffectParams) == 0 || effect.EffectParams[0] <= 0 {
|
||||
return 0, false
|
||||
}
|
||||
return effect.EffectParams[0], true
|
||||
}
|
||||
|
||||
// dropRateBonusPercent 把 buff 的效果翻译成"掉落率增加百分比"。
|
||||
//
|
||||
// 只有 `epic_rarity_item_drop_rate` 这一种效果被认;param 缺省或非正数按"不认"处理
|
||||
// (= 服务端不改掉落),绝不猜。
|
||||
func (effect currentVariousBuffEffect) dropRateBonusPercent() (int64, bool) {
|
||||
return effect.dispatchBonusPercent(currentEpicRarityDropRateEffect)
|
||||
}
|
||||
|
||||
// upgradeSuccessBonusPercent 把 buff 的效果翻译成"强化/增幅成功率增加百分点"。
|
||||
//
|
||||
// 只认 `item_usual_upgrade_success_rate`;param 缺省或非正数按"不认"处理。
|
||||
// 强化与增幅在 PVF 里共用这一个效果名(`[item upgrad buff usual upgrade]` /
|
||||
// `[item upgrad buff amplify upgrade]` 是**道具侧**的 stackable type,不是 buff 效果名),
|
||||
// 所以"加的是强化还是增幅"由道具的 stackable type 决定,见
|
||||
// stackable_use_pvf.go 的 currentUpgradeRatePotionStackableKind。
|
||||
func (effect currentVariousBuffEffect) upgradeSuccessBonusPercent() (int64, bool) {
|
||||
return effect.dispatchBonusPercent(currentUpgradeSuccessRateEffect)
|
||||
}
|
||||
|
||||
// dungeonClearExpBonusPercent 把 buff 的效果翻译成"地下城通关经验 +N%"。
|
||||
func (effect currentVariousBuffEffect) dungeonClearExpBonusPercent() (int64, bool) {
|
||||
return effect.dispatchBonusPercent(currentDungeonClearExpRateEffect)
|
||||
}
|
||||
|
||||
// serverSideStatChangeEffects 是"服务端必须自己算"的效果名。其余效果(城镇移速、
|
||||
// 进图属性…)由客户端按同一份 buff 表自己施加,服务端只负责把掷到的 buff 注册过去。
|
||||
func serverSideStatChangeEffect(effectName string) bool {
|
||||
for _, want := range []string{
|
||||
currentEpicRarityDropRateEffect,
|
||||
currentUpgradeSuccessRateEffect,
|
||||
currentDungeonClearExpRateEffect,
|
||||
} {
|
||||
if strings.EqualFold(strings.TrimSpace(effectName), want) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// clientBuffFamilies 列出本表里"客户端自施放"的那些 family(buff effect name)。
|
||||
//
|
||||
// 用途:登录/重登时要逐个 family 去看角色身上有没有仍然生效的窗口(各族互不覆盖、
|
||||
// 各按自己的到期时间结束),所以需要一份可枚举的清单。
|
||||
func (tables currentVariousBuffTables) clientBuffFamilies() []string {
|
||||
seen := make(map[string]struct{}, 4)
|
||||
for _, rolls := range tables.ByItem {
|
||||
for _, roll := range rolls {
|
||||
effect, found := tables.ByBuff[roll.BuffID]
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
name := strings.ToLower(strings.TrimSpace(effect.EffectName))
|
||||
if name == "" || serverSideStatChangeEffect(name) {
|
||||
continue
|
||||
}
|
||||
seen[name] = struct{}{}
|
||||
}
|
||||
}
|
||||
out := make([]string, 0, len(seen))
|
||||
for name := range seen {
|
||||
out = append(out, name)
|
||||
}
|
||||
sort.Strings(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// statChangePotionGrantableBuffIDs 列出这一族药剂**可能发出去的**全部 buff id
|
||||
// (= etc/variousbuffstackablelist.etc 里出现过的所有 buff)。
|
||||
//
|
||||
// 用于"窗口全过期时把残留图标一次清干净":两族(服务端算的 + 客户端自施放的)都覆盖,
|
||||
// 且只覆盖这一族真会发的 buff,不会误伤别的系统。
|
||||
func (tables currentVariousBuffTables) statChangePotionGrantableBuffIDs() []int32 {
|
||||
seen := make(map[int64]struct{}, 16)
|
||||
for _, rolls := range tables.ByItem {
|
||||
for _, roll := range rolls {
|
||||
if roll.BuffID > 0 && roll.BuffID <= math.MaxInt32 {
|
||||
seen[roll.BuffID] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
out := make([]int32, 0, len(seen))
|
||||
for buffID := range seen {
|
||||
out = append(out, int32(buffID))
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool { return out[i] < out[j] })
|
||||
return out
|
||||
}
|
||||
|
||||
// 服务端要算的三种效果之外,剩下的都归到 family(= buff 的 effect name)下。
|
||||
//
|
||||
// 一件道具只会属于一个 family(赛丽亚那五件各自一族),所以这里取第一组有效候选即可;
|
||||
// 若真的出现同道具多族,只认第一个有效 family,宁可少认也不混族。
|
||||
func (tables currentVariousBuffTables) clientBuffRollsForItem(itemID int64) (string, []currentVariousBuffRoll) {
|
||||
if itemID <= 0 || itemID > math.MaxUint32 {
|
||||
return "", nil
|
||||
}
|
||||
rolls := tables.ByItem[uint32(itemID)]
|
||||
if len(rolls) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
out := make([]currentVariousBuffRoll, 0, len(rolls))
|
||||
family := ""
|
||||
for _, roll := range rolls {
|
||||
if roll.BuffID <= 0 || roll.Weight <= 0 {
|
||||
continue
|
||||
}
|
||||
effect, found := tables.ByBuff[roll.BuffID]
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
name := strings.ToLower(strings.TrimSpace(effect.EffectName))
|
||||
if name == "" || serverSideStatChangeEffect(name) {
|
||||
continue
|
||||
}
|
||||
if family == "" {
|
||||
family = name
|
||||
}
|
||||
if family != name {
|
||||
continue
|
||||
}
|
||||
bonus := int64(0)
|
||||
if len(effect.EffectParams) > 0 {
|
||||
bonus = effect.EffectParams[0]
|
||||
}
|
||||
out = append(out, currentVariousBuffRoll{
|
||||
BuffID: roll.BuffID,
|
||||
Weight: roll.Weight,
|
||||
BonusPercent: bonus,
|
||||
})
|
||||
}
|
||||
if family == "" || len(out) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
return family, out
|
||||
}
|
||||
|
||||
// rollsForItem 返回某道具在**指定效果**上的候选(权重已归一到正数)。
|
||||
//
|
||||
// 一件道具的概率表里可能混着不同效果(例如掉落率 buff 与强化成功率 buff),
|
||||
// 所以按效果名过滤,而不是"返回全部"。
|
||||
func (tables currentVariousBuffTables) rollsForItem(itemID int64, wantEffect string) []currentVariousBuffRoll {
|
||||
if itemID <= 0 || itemID > math.MaxUint32 {
|
||||
return nil
|
||||
}
|
||||
rolls := tables.ByItem[uint32(itemID)]
|
||||
if len(rolls) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]currentVariousBuffRoll, 0, len(rolls))
|
||||
for _, roll := range rolls {
|
||||
if roll.BuffID <= 0 || roll.Weight <= 0 {
|
||||
continue
|
||||
}
|
||||
effect, found := tables.ByBuff[roll.BuffID]
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
if _, ok := effect.dispatchBonusPercent(wantEffect); !ok {
|
||||
continue
|
||||
}
|
||||
out = append(out, roll)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// epicDropRateRollsForItem 返回某道具的"史诗掉落率"候选。
|
||||
func (tables currentVariousBuffTables) epicDropRateRollsForItem(itemID int64) []currentVariousBuffRoll {
|
||||
return tables.rollsForItem(itemID, currentEpicRarityDropRateEffect)
|
||||
}
|
||||
|
||||
// currentVariousBuffTables 惰性读两张 buff 表。
|
||||
//
|
||||
// 读不到(合成测试源、PVF 缺表)返回 ok=false,调用方回退到 `[explain]` 文字判据 ——
|
||||
// 这正是 490000413 这种"表里没有条目"的老活动券所依赖的路径。
|
||||
func (c *pvfDungeonDropCatalog) currentVariousBuffTables() (currentVariousBuffTables, bool) {
|
||||
if c == nil || c.source == nil {
|
||||
return currentVariousBuffTables{}, false
|
||||
}
|
||||
c.variousBuffOnce.Do(func() {
|
||||
c.variousBuffs, c.variousBuffErr = loadCurrentVariousBuffTables(c.source)
|
||||
})
|
||||
if c.variousBuffErr != nil {
|
||||
return currentVariousBuffTables{}, false
|
||||
}
|
||||
if len(c.variousBuffs.ByItem) == 0 && len(c.variousBuffs.ByBuff) == 0 {
|
||||
return currentVariousBuffTables{}, false
|
||||
}
|
||||
return c.variousBuffs, true
|
||||
}
|
||||
Reference in new issue
Block a user