Files
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

171 lines
4.1 KiB
Go

package db
import (
"context"
"errors"
"fmt"
"strings"
"time"
)
// RedisHashBackend 用 Redis hash 实现 HashBackend。
type RedisHashBackend struct {
Executor RedisExecutor
}
// NewRedisHashBackend 创建 Redis hash 后端。
func NewRedisHashBackend(executor RedisExecutor) RedisHashBackend {
return RedisHashBackend{Executor: executor}
}
// LoadHash 从 Redis hash 读取字段。
func (b RedisHashBackend) LoadHash(ctx context.Context, key string, fields []string) (map[string][]byte, bool, error) {
if b.Executor == nil {
return nil, false, errors.New("redis executor is nil")
}
ctx = contextOrBackground(ctx)
key = strings.TrimSpace(key)
if key == "" {
return nil, false, ErrRecordKeyRequired
}
fields = normHashFields(fields)
if len(fields) == 0 {
reply, err := b.Executor.Do(ctx, "HGETALL", key)
if err != nil {
return nil, false, err
}
return redisHGetAllFields(reply)
}
args := make([]any, 0, 1+len(fields))
args = append(args, key)
for _, field := range fields {
args = append(args, field)
}
reply, err := b.Executor.Do(ctx, "HMGET", args...)
if err != nil {
return nil, false, err
}
values, err := redisArray(reply)
if err != nil {
return nil, false, err
}
if len(values) != len(fields) {
return nil, false, fmt.Errorf("redis HMGET returned %d values for %d fields", len(values), len(fields))
}
out := make(map[string][]byte, len(fields))
for i, value := range values {
data, ok, err := redisBulkBytes(value)
if err != nil {
return nil, false, err
}
if ok {
out[fields[i]] = data
}
}
return out, len(out) > 0, nil
}
// SaveHash 保存 Redis hash 字段。
func (b RedisHashBackend) SaveHash(ctx context.Context, key string, fields map[string][]byte, ttl time.Duration) error {
return SaveRedisHashFields(ctx, b.Executor, key, fields, ttl)
}
// SaveHashBatch 批量保存 Redis hash 字段。
func (b RedisHashBackend) SaveHashBatch(ctx context.Context, batches []HashSaveBatch) error {
merged, err := mergeHashSaveBatches(batches)
if err != nil {
return err
}
redisBatches := make([]RedisHashBatch, 0, len(merged))
for _, batch := range merged {
redisBatches = append(redisBatches, RedisHashBatch(batch))
}
return SaveRedisHashFieldBatches(ctx, b.Executor, redisBatches)
}
// Check 通过 PING 检查 Redis 连接。
func (b RedisHashBackend) Check(ctx context.Context) error {
if b.Executor == nil {
return errors.New("redis executor is nil")
}
ctx = contextOrBackground(ctx)
_, err := b.Executor.Do(ctx, "PING")
return err
}
func redisHGetAllFields(reply any) (map[string][]byte, bool, error) {
values, err := redisArray(reply)
if err != nil {
return nil, false, err
}
if len(values)%2 != 0 {
return nil, false, fmt.Errorf("redis HGETALL returned odd value count %d", len(values))
}
out := make(map[string][]byte, len(values)/2)
for i := 0; i < len(values); i += 2 {
name, ok, err := redisBulkString(values[i])
if err != nil {
return nil, false, err
}
if !ok {
continue
}
data, ok, err := redisBulkBytes(values[i+1])
if err != nil {
return nil, false, err
}
if ok {
out[name] = data
}
}
return out, len(out) > 0, nil
}
func redisArray(reply any) ([]any, error) {
switch value := reply.(type) {
case nil:
return nil, nil
case []any:
return value, nil
case [][]byte:
out := make([]any, len(value))
for i, item := range value {
if item == nil {
out[i] = nil
} else {
out[i] = append([]byte(nil), item...)
}
}
return out, nil
case []string:
out := make([]any, len(value))
for i, item := range value {
out[i] = item
}
return out, nil
default:
return nil, fmt.Errorf("unexpected redis array reply %T", reply)
}
}
func redisBulkString(value any) (string, bool, error) {
data, ok, err := redisBulkBytes(value)
if err != nil || !ok {
return "", ok, err
}
return string(data), true, nil
}
func redisBulkBytes(value any) ([]byte, bool, error) {
switch v := value.(type) {
case nil:
return nil, false, nil
case []byte:
return append([]byte(nil), v...), true, nil
case string:
return []byte(v), true, nil
default:
return nil, false, fmt.Errorf("unexpected redis bulk reply %T", value)
}
}