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,162 @@
|
||||
package datatable
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrRegionRequired = errors.New("data table region is required")
|
||||
ErrBaselineNotFound = errors.New("data table baseline region not found")
|
||||
ErrRegionDiffRequires2 = errors.New("data table region diff requires at least two regions")
|
||||
)
|
||||
|
||||
type RegionInput struct {
|
||||
Name string `json:"name"`
|
||||
Root string `json:"root"`
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
type RegionSnapshot struct {
|
||||
Name string `json:"name"`
|
||||
Root string `json:"root"`
|
||||
Version string `json:"version,omitempty"`
|
||||
Manifest Manifest `json:"manifest"`
|
||||
}
|
||||
|
||||
type RegionDiff struct {
|
||||
Region string `json:"region"`
|
||||
Baseline string `json:"baseline"`
|
||||
Manifest Manifest `json:"manifest"`
|
||||
BaselineManifest Manifest `json:"baseline_manifest"`
|
||||
Diff Diff `json:"diff"`
|
||||
}
|
||||
|
||||
type RegionDiffSummary struct {
|
||||
Regions int `json:"regions"`
|
||||
Compared int `json:"compared"`
|
||||
Baseline string `json:"baseline"`
|
||||
BaselineChecksum string `json:"baseline_checksum,omitempty"`
|
||||
Consistent bool `json:"consistent"`
|
||||
DriftedRegions []string `json:"drifted_regions,omitempty"`
|
||||
Added int `json:"added"`
|
||||
Changed int `json:"changed"`
|
||||
Removed int `json:"removed"`
|
||||
Unchanged int `json:"unchanged"`
|
||||
}
|
||||
|
||||
type RegionReport struct {
|
||||
Baseline string `json:"baseline"`
|
||||
Regions []RegionSnapshot `json:"regions"`
|
||||
Diffs []RegionDiff `json:"diffs"`
|
||||
Summary RegionDiffSummary `json:"summary"`
|
||||
GeneratedAt time.Time `json:"generated_at"`
|
||||
}
|
||||
|
||||
func CompareRegions(ctx context.Context, regions []RegionInput, baseline string) (RegionReport, error) {
|
||||
if err := contextErr(ctx); err != nil {
|
||||
return RegionReport{}, err
|
||||
}
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
if len(regions) == 0 {
|
||||
return RegionReport{}, ErrRegionRequired
|
||||
}
|
||||
if len(regions) < 2 {
|
||||
return RegionReport{}, ErrRegionDiffRequires2
|
||||
}
|
||||
|
||||
datasets := make(map[string]Dataset, len(regions))
|
||||
snapshots := make([]RegionSnapshot, 0, len(regions))
|
||||
for _, input := range regions {
|
||||
normalized, err := normalizeRegionInput(input)
|
||||
if err != nil {
|
||||
return RegionReport{}, err
|
||||
}
|
||||
if _, exists := datasets[normalized.Name]; exists {
|
||||
return RegionReport{}, fmt.Errorf("%w: duplicate region %s", ErrRegionRequired, normalized.Name)
|
||||
}
|
||||
dataset, err := loadDirectory(ctx, normalized.Root, normalized.Version)
|
||||
if err != nil {
|
||||
return RegionReport{}, fmt.Errorf("load data table region %s: %w", normalized.Name, err)
|
||||
}
|
||||
datasets[normalized.Name] = dataset
|
||||
snapshots = append(snapshots, RegionSnapshot{
|
||||
Name: normalized.Name,
|
||||
Root: normalized.Root,
|
||||
Version: normalized.Version,
|
||||
Manifest: dataset.Manifest(),
|
||||
})
|
||||
if strings.TrimSpace(baseline) == "" {
|
||||
baseline = normalized.Name
|
||||
}
|
||||
}
|
||||
|
||||
baseline = strings.TrimSpace(baseline)
|
||||
base, ok := datasets[baseline]
|
||||
if !ok {
|
||||
return RegionReport{}, fmt.Errorf("%w: %s", ErrBaselineNotFound, baseline)
|
||||
}
|
||||
|
||||
sort.Slice(snapshots, func(i, j int) bool { return snapshots[i].Name < snapshots[j].Name })
|
||||
report := RegionReport{
|
||||
Baseline: baseline,
|
||||
Regions: snapshots,
|
||||
GeneratedAt: time.Now().UTC(),
|
||||
}
|
||||
report.Summary = RegionDiffSummary{
|
||||
Regions: len(snapshots),
|
||||
Baseline: baseline,
|
||||
BaselineChecksum: base.Manifest().Checksum,
|
||||
Consistent: true,
|
||||
}
|
||||
|
||||
for _, snapshot := range snapshots {
|
||||
if snapshot.Name == baseline {
|
||||
continue
|
||||
}
|
||||
dataset := datasets[snapshot.Name]
|
||||
diff := diffTables(base.tables, dataset.tables)
|
||||
regionDiff := RegionDiff{
|
||||
Region: snapshot.Name,
|
||||
Baseline: baseline,
|
||||
Manifest: dataset.Manifest(),
|
||||
BaselineManifest: base.Manifest(),
|
||||
Diff: cloneDiff(diff),
|
||||
}
|
||||
report.Diffs = append(report.Diffs, regionDiff)
|
||||
report.Summary.Compared++
|
||||
report.Summary.Added += len(diff.Added)
|
||||
report.Summary.Changed += len(diff.Changed)
|
||||
report.Summary.Removed += len(diff.Removed)
|
||||
report.Summary.Unchanged += len(diff.Unchanged)
|
||||
if diff.HasChanges() {
|
||||
report.Summary.Consistent = false
|
||||
report.Summary.DriftedRegions = append(report.Summary.DriftedRegions, snapshot.Name)
|
||||
}
|
||||
}
|
||||
sort.Strings(report.Summary.DriftedRegions)
|
||||
return report, nil
|
||||
}
|
||||
|
||||
func normalizeRegionInput(input RegionInput) (RegionInput, error) {
|
||||
input.Name = strings.TrimSpace(input.Name)
|
||||
input.Root = strings.TrimSpace(input.Root)
|
||||
input.Version = strings.TrimSpace(input.Version)
|
||||
if input.Root == "" {
|
||||
return RegionInput{}, ErrDirectoryRequired
|
||||
}
|
||||
if input.Name == "" {
|
||||
input.Name = filepath.Base(filepath.Clean(input.Root))
|
||||
}
|
||||
if input.Name == "" || input.Name == "." || input.Name == string(filepath.Separator) {
|
||||
return RegionInput{}, ErrRegionRequired
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
Reference in new issue
Block a user