DNF90 服务端与启动器完整源码(含 GM 工具功能增强)

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

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

No files matched your search

@@ -0,0 +1,139 @@
package storageobject
import (
"context"
"encoding/json"
"strconv"
"time"
)
const archiveRecordMetaKey = "longheng-archive-record"
type CloudObjectClient interface {
PutObject(context.Context, string, []byte, map[string]string) error
GetObject(context.Context, string) ([]byte, map[string]string, bool, error)
DeleteObject(context.Context, string) error
}
type CloudArchiveStore struct {
client CloudObjectClient
now func() time.Time
}
func NewCloudArchiveStore(client CloudObjectClient, now func() time.Time) (*CloudArchiveStore, error) {
if client == nil {
return nil, ErrInvalidArchiveKey
}
if now == nil {
now = time.Now
}
return &CloudArchiveStore{client: client, now: now}, nil
}
func (s *CloudArchiveStore) Put(ctx context.Context, key string, data []byte, meta map[string]string) (ArchiveRecord, error) {
if err := ctxErr(ctx); err != nil {
return ArchiveRecord{}, err
}
if s == nil || s.client == nil {
return ArchiveRecord{}, ErrInvalidArchiveKey
}
normalized, err := normalizeArchiveKey(key)
if err != nil {
return ArchiveRecord{}, err
}
data = append([]byte(nil), data...)
record := ArchiveRecord{
Key: normalized,
Size: int64(len(data)),
SHA256: sha256Hex(data),
Meta: cloneArchiveMeta(meta),
UpdatedAt: s.now().UTC(),
}
objectMeta, err := encodeArchiveMeta(record)
if err != nil {
return ArchiveRecord{}, err
}
if err := s.client.PutObject(ctx, normalized, data, objectMeta); err != nil {
return ArchiveRecord{}, err
}
return record, nil
}
func (s *CloudArchiveStore) Get(ctx context.Context, key string) ([]byte, ArchiveRecord, bool, error) {
if err := ctxErr(ctx); err != nil {
return nil, ArchiveRecord{}, false, err
}
if s == nil || s.client == nil {
return nil, ArchiveRecord{}, false, ErrInvalidArchiveKey
}
normalized, err := normalizeArchiveKey(key)
if err != nil {
return nil, ArchiveRecord{}, false, err
}
data, meta, ok, err := s.client.GetObject(ctx, normalized)
if err != nil || !ok {
return nil, ArchiveRecord{}, ok, err
}
record := decodeArchiveMeta(normalized, data, meta)
if record.SHA256 != "" && record.SHA256 != sha256Hex(data) {
return nil, ArchiveRecord{}, false, ErrArchiveCorrupt
}
return append([]byte(nil), data...), record, true, nil
}
func (s *CloudArchiveStore) Delete(ctx context.Context, key string) error {
if err := ctxErr(ctx); err != nil {
return err
}
if s == nil || s.client == nil {
return ErrInvalidArchiveKey
}
normalized, err := normalizeArchiveKey(key)
if err != nil {
return err
}
return s.client.DeleteObject(ctx, normalized)
}
func encodeArchiveMeta(record ArchiveRecord) (map[string]string, error) {
meta := cloneArchiveMeta(record.Meta)
data, err := json.Marshal(record)
if err != nil {
return nil, err
}
if meta == nil {
meta = make(map[string]string, 1)
}
meta[archiveRecordMetaKey] = string(data)
return meta, nil
}
func decodeArchiveMeta(key string, data []byte, meta map[string]string) ArchiveRecord {
if raw := meta[archiveRecordMetaKey]; raw != "" {
var record ArchiveRecord
if err := json.Unmarshal([]byte(raw), &record); err == nil && record.Key != "" {
record.Meta = cloneArchiveMeta(record.Meta)
return record
}
}
return ArchiveRecord{
Key: key,
Size: int64(len(data)),
SHA256: sha256Hex(data),
Meta: cloneArchiveMeta(meta),
UpdatedAt: parseArchiveTime(meta["updated_at"]),
}
}
func parseArchiveTime(value string) time.Time {
if value == "" {
return time.Time{}
}
if ts, err := time.Parse(time.RFC3339Nano, value); err == nil {
return ts.UTC()
}
if unixNano, err := strconv.ParseInt(value, 10, 64); err == nil && unixNano > 0 {
return time.Unix(0, unixNano).UTC()
}
return time.Time{}
}