Files
90CNServer/server/pkg/serializer/serializer.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

180 lines
3.9 KiB
Go

package serializer
import (
"encoding/json"
"errors"
"fmt"
"sort"
"strings"
"sync"
"google.golang.org/protobuf/proto"
)
var (
ErrUnknownSerializer = errors.New("unknown serializer")
ErrInvalidTarget = errors.New("invalid serializer target")
)
type Serializer interface {
Name() string
Marshal(any) ([]byte, error)
Unmarshal([]byte, any) error
}
type Registry struct {
mu sync.RWMutex
serializers map[string]Serializer
}
func NewRegistry(serializers ...Serializer) *Registry {
r := &Registry{serializers: make(map[string]Serializer)}
for _, serializer := range serializers {
_ = r.Register(serializer)
}
return r
}
func (r *Registry) Register(serializer Serializer) error {
if serializer == nil {
return ErrUnknownSerializer
}
name := normalizeName(serializer.Name())
if name == "" {
return ErrUnknownSerializer
}
r.mu.Lock()
r.serializers[name] = serializer
r.mu.Unlock()
return nil
}
func (r *Registry) Get(name string) (Serializer, bool) {
if r == nil {
return nil, false
}
name = normalizeName(name)
r.mu.RLock()
serializer, ok := r.serializers[name]
r.mu.RUnlock()
return serializer, ok
}
func (r *Registry) MustGet(name string) (Serializer, error) {
serializer, ok := r.Get(name)
if !ok {
return nil, fmt.Errorf("%w: %s", ErrUnknownSerializer, name)
}
return serializer, nil
}
func (r *Registry) Names() []string {
if r == nil {
return nil
}
r.mu.RLock()
names := make([]string, 0, len(r.serializers))
for name := range r.serializers {
names = append(names, name)
}
r.mu.RUnlock()
sort.Strings(names)
return names
}
type JSON struct{}
func (JSON) Name() string { return "json" }
func (JSON) Marshal(value any) ([]byte, error) {
return json.Marshal(value)
}
func (JSON) Unmarshal(data []byte, target any) error {
if target == nil {
return ErrInvalidTarget
}
return json.Unmarshal(data, target)
}
type Raw struct{}
func (Raw) Name() string { return "raw" }
func (Raw) Marshal(value any) ([]byte, error) {
switch typed := value.(type) {
case []byte:
return append([]byte(nil), typed...), nil
case string:
return []byte(typed), nil
default:
return nil, fmt.Errorf("%w: raw serializer requires []byte or string", ErrInvalidTarget)
}
}
func (Raw) Unmarshal(data []byte, target any) error {
switch typed := target.(type) {
case *[]byte:
if typed == nil {
return ErrInvalidTarget
}
*typed = append((*typed)[:0], data...)
return nil
case *string:
if typed == nil {
return ErrInvalidTarget
}
*typed = string(data)
return nil
default:
return fmt.Errorf("%w: raw serializer target must be *[]byte or *string", ErrInvalidTarget)
}
}
type Protobuf struct {
DiscardUnknown bool
}
func (Protobuf) Name() string { return "protobuf" }
func (Protobuf) Marshal(value any) ([]byte, error) {
msg, ok := value.(proto.Message)
if !ok {
return nil, fmt.Errorf("%w: protobuf serializer requires proto.Message", ErrInvalidTarget)
}
return proto.Marshal(msg)
}
func (p Protobuf) Unmarshal(data []byte, target any) error {
if target == nil {
return ErrInvalidTarget
}
msg, ok := target.(proto.Message)
if !ok {
return fmt.Errorf("%w: protobuf serializer target must be proto.Message", ErrInvalidTarget)
}
// 协议层已经限制单包大小;这里再限制嵌套深度,避免恶意递归消息拖垮解码栈。
return proto.UnmarshalOptions{
DiscardUnknown: p.DiscardUnknown,
RecursionLimit: 32,
}.Unmarshal(data, msg)
}
type LenientProtobuf struct{}
func (LenientProtobuf) Name() string { return "protobuf_lenient" }
func (LenientProtobuf) Marshal(value any) ([]byte, error) {
return (Protobuf{}).Marshal(value)
}
func (LenientProtobuf) Unmarshal(data []byte, target any) error {
return (Protobuf{DiscardUnknown: true}).Unmarshal(data, target)
}
var DefaultRegistry = NewRegistry(JSON{}, Raw{}, Protobuf{}, LenientProtobuf{})
func normalizeName(name string) string {
return strings.ToLower(strings.TrimSpace(name))
}