深入剖析 Fantasy 框架的消息设计与序列化机制:协同架构下的高效转换与场景适配

深入剖析 Fantasy 框架的消息设计与序列化机制

引言:消息与序列化的分布式通信基石

在分布式系统中,消息是组件间通信的 "语言",而序列化则是 "语言翻译器"------ 它将内存中的业务对象转换为可传输的字节流,同时将接收的字节流还原为可操作的对象。Fantasy框架作为面向分布式场景的开发框架,其消息设计与序列化机制经过精心打磨,既保证了跨平台通信的兼容性,又兼顾了高性能与扩展性,成为连接分布式节点的 "神经传导系统"。

本文将从源码层面深度剖析Fantasy框架的消息设计与序列化机制,围绕消息接口抽象、序列化器实现、类型映射管理、多协议适配等核心维度,拆解从业务对象到网络字节流的完整转换链路。

框架设计概览:消息与序列化的整体架构

Fantasy 框架的消息与序列化机制采用「消息抽象 - 序列化实现 - 全局调度」的三层架构,通过 "接口规范 + 抽象类实现" 的组合设计,实现了消息格式定义与序列化逻辑的解耦,同时兼顾跨场景通信的灵活性与高效性。

核心组件关系图谱

从代码结构来看,整个机制的核心组件可归纳为以下四类,涵盖消息与序列化的全流程,且明确体现了 "接口 + 抽象类" 的协同设计:

  • 消息抽象层 :以IMessage为核心的接口体系定义消息的行为契约(如 OpCode() 标识、请求 / 响应分类);以 AMessage 为核心的抽象类体系提供消息的基础功能实现(序列化生命周期管理、对象池支持、场景上下文关联等)
  • 序列化层ISerialize 抽象接口定义序列化的通用规范;ProtobufSerializerBsonSerializer 等具体实现适配不同协议,负责将消息对象与字节流互转。
  • 管理层MessageDispatcherComponent 管理 "消息类型 - OpCode - 处理器" 的映射关系,实现消息的路由与分发;SerializerManager 负责序列化器的注册与调度,根据协议类型匹配对应的转换工具,是 "谁来协调" 的核心。
  • 辅助层OpCode 体系关联消息类型与唯一标识,为跨节点识别提供依据;MemoryStreamBuffer 实现内存流的池化管理,减少序列化过程中的 GC 压力;CreateInstance 优化反射性能,提升消息对象的创建效率。

这些组件的协作流程可概括为:
业务消息对象(实现IMessage+继承AMessage)MessageDispatcherComponent 匹配 OpCode 与序列化器SerializerManager 调用对应序列化器Serialize 编码为字节流网络传输Deserialize 解码为消息对象MessageDispatcherComponent 分发至处理器

消息体系的双重抽象:IMessage 与 AMessage

消息是分布式通信的基本单位,Fantasy 框架对消息的抽象采用 "接口规范 + 抽象类实现" 的双层设计,通过 IMessage 接口与 AMessage 抽象类的配合,实现了消息 "行为规范" 与 "基础功能" 的解耦。

IMessage:消息的行为契约与功能分类

IMessage是框架对消息 "行为规范" 的核心抽象(本质为接口)。它不提供具体实现,而是从 "what to do" 的角度,规定所有消息必须遵循的基础契约,其源码位于Runtime/Core/Network/Interface/IMessage.cs

csharp 复制代码
using System;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member

namespace Fantasy.Network.Interface
{
    /// <summary>
    /// 表示通用消息接口。
    /// </summary>
    public interface IMessage
    {
        /// <summary>
        /// 获取消息的操作代码。
        /// </summary>
        /// <returns>操作代码。</returns>
        uint OpCode();
    }

    /// <summary>
    /// 表示请求消息接口。
    /// </summary>
    public interface IRequest : IMessage
    {
        
    }

    /// <summary>
    /// 表示响应消息接口。
    /// </summary>
    public interface IResponse : IMessage
    {
        /// <summary>
        /// 获取或设置错误代码。
        /// </summary>
        uint ErrorCode { get; set; }
    }
    // 普通路由消息
    /// <summary>
    /// 表示普通路由消息的接口,继承自请求接口。
    /// </summary>
    public interface IRouteMessage : IRequest
    {
        
    }

    /// <summary>
    /// 普通路由请求接口,继承自普通路由消息接口。
    /// </summary>
    public interface IRouteRequest : IRouteMessage { }
    /// <summary>
    /// 普通路由响应接口,继承自响应接口。
    /// </summary>
    public interface IRouteResponse : IResponse { }
    // 可寻址协议
    /// <summary>
    /// 表示可寻址协议的普通路由消息接口,继承自普通路由消息接口。
    /// </summary>
    public interface IAddressableRouteMessage : IRouteMessage { }
    /// <summary>
    /// 可寻址协议的普通路由请求接口,继承自可寻址协议的普通路由消息接口。
    /// </summary>
    public interface IAddressableRouteRequest : IRouteRequest { }
    /// <summary>
    /// 可寻址协议的普通路由响应接口,继承自普通路由响应接口。
    /// </summary>
    public interface IAddressableRouteResponse : IRouteResponse { }
    // 自定义Route协议
    public interface ICustomRoute : IMessage
    {
        int RouteType { get; }
    }
    /// <summary>
    /// 表示自定义Route协议的普通路由消息接口,继承自普通路由消息接口。
    /// </summary>
    public interface ICustomRouteMessage : IRouteMessage, ICustomRoute { }
    /// <summary>
    /// 自定义Route协议的普通路由请求接口,继承自自定义Route协议的普通路由消息接口。
    /// </summary>
    public interface ICustomRouteRequest : IRouteRequest, ICustomRoute { }
    /// <summary>
    /// 自定义Route协议的普通路由响应接口,继承自普通路由响应接口。
    /// </summary>
    public interface ICustomRouteResponse : IRouteResponse { }
    /// <summary>
    /// 表示漫游协议的普通路由消息接口,继承自普通路由消息接口。
    /// </summary>
    public interface IRoamingMessage : IRouteMessage, ICustomRoute { }
    /// <summary>
    /// 漫游协议的普通路由请求接口,继承自自定义Route协议的普通路由消息接口。
    /// </summary>
    public interface IRoamingRequest : IRoamingMessage { }
    /// <summary>
    /// 漫游协议的普通路由响应接口,继承自普通路由响应接口。
    /// </summary>
    public interface IRoamingResponse : IRouteResponse { }
}
接口设计的核心价值:从功能约定到通信规范

类型标识标准化 :强制定义OpCode()方法,要求所有消息必须返回唯一的操作码(Opcode)。这是消息路由的基础 ------ 框架通过 Opcode 可快速识别消息类型,将其分发到对应的处理器,避免基于类型判断的冗余逻辑。

功能分类的扩展载体 :作为基础接口,IMessage 衍生出多个子接口以区分消息的业务类型:

  • IRouteMessage及衍生接口(IAddressableRouteMessage等)为分布式系统提供路由能力,支持跨节点消息传递(如从网关服务器路由至业务服务器);
  • IRequestIResponse构建 "请求 - 响应" 模式,IResponseErrorCode字段统一标准化错误反馈(如 0 表示成功,非 0 对应具体错误类型);

功能分类与扩展灵活性:接口继承实现功能分类,使框架可对不同消息类型差异化处理(如请求消息等待响应、路由消息携带路由信息),业务层新增消息类型时仅需实现对应子接口即可被框架识别,兼顾处理针对性与扩展灵活性。

AMessage:消息的基础功能实现与序列化支持

IMessage 的 "规范定义" 不同,专注于提供 "how to do" 的基础功能实现,是消息 "物理特性" 的载体。其抽象类源码位于Runtime/Core/Network/Interface/ASerialize.cs

csharp 复制代码
using System;
using System.ComponentModel;
using System.Runtime.Serialization;
using Fantasy.Pool;
#if FANTASY_NET || FANTASY_UNITY || FANTASY_CONSOLE
using MongoDB.Bson.Serialization.Attributes;
#endif
using Newtonsoft.Json;
using ProtoBuf;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.

namespace Fantasy.Serialize
{
    public abstract class ASerialize : ISupportInitialize, IDisposable
    {
        public virtual void Dispose() { }
        public virtual void BeginInit() { }
        public virtual void EndInit() { }
        public virtual void AfterDeserialization() => EndInit();
    }

    public abstract class AMessage : ASerialize, IPool
    {
#if FANTASY_NET || FANTASY_UNITY || FANTASY_CONSOLE
        [BsonIgnore] 
        [JsonIgnore] 
        [IgnoreDataMember] 
        [ProtoIgnore]
        private Scene _scene;
        protected Scene GetScene()
        {
            return _scene;
        }

        public void SetScene(Scene scene)
        {
            _scene = scene;
        }
#endif
#if FANTASY_NET
        [BsonIgnore] 
#endif
        [JsonIgnore] 
        [IgnoreDataMember] 
        [ProtoIgnore]
        private bool _isPool;

        public bool IsPool()
        {
            return _isPool;
        }

        public void SetIsPool(bool isPool)
        {
            _isPool = isPool;
        }
    }
}
ASerialize:序列化生命周期与资源管理的基础载体

与后续 AMessage 的 "消息特性封装" 不同,ASerialize 作为底层基础,标准化了可序列化对象的生命周期与资源管理;AMessage 在此之上聚焦消息特性,集成序列化适配、池化管理与场景绑定能力,协同为框架的消息处理提供稳定且灵活的基础支撑。

ASerialize 的核心价值:序列化流程与资源管理的基础规范

序列化生命周期管理 :实现 ISupportInitialize 接口,提供 BeginInit()EndInit() 方法,规范了对象在序列化 / 反序列化过程中的初始化流程。其中 AfterDeserialization() 方法直接关联 EndInit(),确保对象在反序列化完成后能自动执行后续初始化逻辑(如字段校验、状态恢复),为上层序列化框架(如 ProtoBufJSON)提供了统一的生命周期钩子。

资源自动释放支持 :实现 IDisposable 接口,定义 Dispose() 虚方法,为所有子类提供了资源释放的标准化入口。在需要手动管理非托管资源(如网络连接句柄、临时缓存)的场景中,子类可重写该方法实现资源回收,避免内存泄漏。

序列化框架适配基础 :作为所有可序列化对象的基类,ASerialize 奠定了与各类序列化框架(ProtoBufJSONMongoDB.Bson 等)的适配基础。其通过虚方法设计(如 EndInit()Dispose())为子类预留了扩展点,使得派生类能够在不破坏基础规范的前提下,集成具体的序列化注解与逻辑。

AMessage:消息的基础功能实现与序列化支持

继承自 ASerialize,与 IMessage 的 "规范定义" 不同,AMessage 专注于提供 "how to do" 的基础功能实现,是消息 "物理特性" 的载体,在 ASerialize 的基础上进一步封装了消息特有的核心能力。

AMessage 的核心支撑:从序列化适配到场景协同

序列化能力集成 :基于 ASerialize 的底层支撑,内置与主流序列化框架(如 ProtoBufJSON)的适配逻辑。所有继承 AMessage 的消息类无需额外编码即可支持序列化 / 反序列化操作,极大简化了消息在网络传输、存储中的处理流程。

对象池化管理 :实现 IPool 接口,提供 IsPool()SetIsPool() 等方法,支持消息对象的池化复用。在高频消息交互场景(如游戏中的帧同步、实时通信)中,可减少对象频繁创建 / 销毁带来的内存开销,提升系统性能。

场景上下文绑定 :包含 SetScene()GetScene() 等方法,支持消息与特定场景(Scene)的关联。在多场景架构(如游戏中的多地图、分布式系统中的多服务节点)中,可快速定位消息的上下文环境,简化业务逻辑中的场景依赖处理。

协同模式:组合构建完整消息类型

IMessageAMessage 属于不同层面的抽象,二者无直接继承关系;在实际业务中,具体消息类通过 "继承 AMessage + 实现 IMessage 接口" 的组合方式,同时获得 "规范约束" 与 "功能实现",形成完整的消息类型。

具体消息类的核心价值:框架契约落地与业务通信的场景化实现

具体消息类是框架抽象与业务通信的 "连接纽带":遵循 IMessage 接口与 AMessage 基类的设计规则,通过业务字段(具体消息类中定义的字段)与场景化接口(IMessage 派生接口),将框架通用能力转化为实际通信载体。"抽象规范 + 具体实现" 的结合,保证了所有消息在解析、序列化、分发等流程上的一致性,同时精准匹配分布式系统中的各类通信需求(如请求 - 响应、跨节点路由等)。

这些具体消息覆盖了分布式通信的典型场景,包括:基础通信类(如BenchmarkMessage用于性能测试、PingRequest/PingResponse用于节点心跳检测);地址管理类(如I_AddressableAdd_Request/I_AddressableGet_Response用于分布式对象的跨节点定位);漫游场景类(如I_LinkRoamingRequest/I_UnLinkRoamingResponse用于对象在节点间迁移时的通信链路维护)等,每个消息都通过特定字段与接口实现,精准匹配对应业务需求。其源码位于Runtime/Core/Network/Message/InnerMessage.cs

csharp 复制代码
using Fantasy.Network.Interface;
using Fantasy.Serialize;
using MongoDB.Bson.Serialization.Attributes;
using ProtoBuf;
#if FANTASY_NET
using Fantasy.Network.Roaming;
#endif
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
// ReSharper disable InconsistentNaming
// ReSharper disable PropertyCanBeMadeInitOnly.Global
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member

namespace Fantasy.InnerMessage
{
    [ProtoContract]
    public sealed partial class BenchmarkMessage : AMessage, IMessage
    {
        public uint OpCode()
        {
            return Fantasy.Network.OpCode.BenchmarkMessage;
        }
    }
    [ProtoContract]
    public partial class BenchmarkRequest : AMessage, IRequest
    {
        public uint OpCode()
        {
            return Fantasy.Network.OpCode.BenchmarkRequest;
        }
        [ProtoIgnore] 
        public BenchmarkResponse ResponseType { get; set; }
        [ProtoMember(1)]
        public long RpcId { get; set; }
    }
    
    [ProtoContract]
    public partial class BenchmarkResponse : AMessage, IResponse
    {
        public uint OpCode()
        {
            return Fantasy.Network.OpCode.BenchmarkResponse;
        }
        [ProtoMember(1)]
        public long RpcId { get; set; }
        [ProtoMember(2)]
        public uint ErrorCode { get; set; }
    }
    public sealed partial class Response : AMessage, IResponse
    {
        public uint OpCode()
        {
            return Fantasy.Network.OpCode.DefaultResponse;
        }
        [ProtoMember(1)]
        public long RpcId { get; set; }
        [ProtoMember(2)]
        public uint ErrorCode { get; set; }
    }
    [ProtoContract]
    public sealed partial class RouteResponse : AMessage, IRouteResponse
    {
        public uint OpCode()
        {
            return Fantasy.Network.OpCode.DefaultRouteResponse;
        }
        [ProtoMember(1)]
        public long RpcId { get; set; }
        [ProtoMember(2)]
        public uint ErrorCode { get; set; }
    }
    [ProtoContract]
    public partial class PingRequest : AMessage, IRequest
    {
        public uint OpCode()
        {
            return Fantasy.Network.OpCode.PingRequest;
        }
        [ProtoIgnore] 
        public PingResponse ResponseType { get; set; }
        [ProtoMember(1)]
        public long RpcId { get; set; }
    }
    
    [ProtoContract]
    public partial class PingResponse : AMessage, IResponse
    {
        public uint OpCode()
        {
            return Fantasy.Network.OpCode.PingResponse;
        }
        [ProtoMember(1)]
        public long RpcId { get; set; }
        [ProtoMember(2)]
        public uint ErrorCode { get; set; }
        [ProtoMember(3)]
        public long Now;
    }
    [ProtoContract]
    public partial class I_AddressableAdd_Request : AMessage, IRouteRequest
    {
        [ProtoIgnore]
        public I_AddressableAdd_Response ResponseType { get; set; }
        public uint OpCode() { return Fantasy.Network.OpCode.AddressableAddRequest; }
        public long RouteTypeOpCode() { return 1; }
        [ProtoMember(1)]
        public long AddressableId { get; set; }
        [ProtoMember(2)]
        public long RouteId { get; set; }
        [ProtoMember(3)]
        public bool IsLock { get; set; }
    }
    [ProtoContract]
    public partial class I_AddressableAdd_Response : AMessage, IRouteResponse
    {
        public uint OpCode() { return Fantasy.Network.OpCode.AddressableAddResponse; }
        [ProtoMember(1)]
        public uint ErrorCode { get; set; }
    }
    [ProtoContract]
    public partial class I_AddressableGet_Request : AMessage, IRouteRequest
    {
        [ProtoIgnore]
        public I_AddressableGet_Response ResponseType { get; set; }
        public uint OpCode() { return Fantasy.Network.OpCode.AddressableGetRequest; }
        public long RouteTypeOpCode() { return 1; }
        [ProtoMember(1)]
        public long AddressableId { get; set; }
    }
    [ProtoContract]
    public partial class I_AddressableGet_Response : AMessage, IRouteResponse
    {
        public uint OpCode() { return Fantasy.Network.OpCode.AddressableGetResponse; }
        [ProtoMember(2)]
        public uint ErrorCode { get; set; }
        [ProtoMember(1)]
        public long RouteId { get; set; }
    }
    [ProtoContract]
    public partial class I_AddressableRemove_Request : AMessage, IRouteRequest
    {
        [ProtoIgnore]
        public I_AddressableRemove_Response ResponseType { get; set; }
        public uint OpCode() { return Fantasy.Network.OpCode.AddressableRemoveRequest; }
        public long RouteTypeOpCode() { return 1; }
        [ProtoMember(1)]
        public long AddressableId { get; set; }
    }
    [ProtoContract]
    public partial class I_AddressableRemove_Response : AMessage, IRouteResponse
    {
        public uint OpCode() { return Fantasy.Network.OpCode.AddressableRemoveResponse; }
        [ProtoMember(1)]
        public uint ErrorCode { get; set; }
    }
    [ProtoContract]
    public partial class I_AddressableLock_Request : AMessage, IRouteRequest
    {
        [ProtoIgnore]
        public I_AddressableLock_Response ResponseType { get; set; }
        public uint OpCode() { return Fantasy.Network.OpCode.AddressableLockRequest; }
        public long RouteTypeOpCode() { return 1; }
        [ProtoMember(1)]
        public long AddressableId { get; set; }
    }
    [ProtoContract]
    public partial class I_AddressableLock_Response : AMessage, IRouteResponse
    {
        public uint OpCode() { return Fantasy.Network.OpCode.AddressableLockResponse; }
        [ProtoMember(1)]
        public uint ErrorCode { get; set; }
    }
    [ProtoContract]
    public partial class I_AddressableUnLock_Request : AMessage, IRouteRequest
    {
        [ProtoIgnore]
        public I_AddressableUnLock_Response ResponseType { get; set; }
        public uint OpCode() { return Fantasy.Network.OpCode.AddressableUnLockRequest; }
        public long RouteTypeOpCode() { return 1; }
        [ProtoMember(1)]
        public long AddressableId { get; set; }
        [ProtoMember(2)]
        public long RouteId { get; set; }
        [ProtoMember(3)]
        public string Source { get; set; }
    }
    [ProtoContract]
    public partial class I_AddressableUnLock_Response : AMessage, IRouteResponse
    {
        public uint OpCode() { return Fantasy.Network.OpCode.AddressableUnLockResponse; }
        [ProtoMember(1)]
        public uint ErrorCode { get; set; }
    }
#if FANTASY_NET
    [ProtoContract]
    public sealed class I_LinkRoamingRequest : AMessage, IRouteRequest
    {
        [ProtoIgnore]
        public I_LinkRoamingResponse ResponseType { get; set; }
        public uint OpCode() { return Fantasy.Network.OpCode.LinkRoamingRequest; }
        public long RouteTypeOpCode() { return 1; }
        [ProtoMember(1)]
        public long RoamingId { get; set; }
        [ProtoMember(2)]
        public int RoamingType { get; set; }
        [ProtoMember(3)]
        public long ForwardSessionRouteId { get; set; }
        [ProtoMember(4)]
        public long SceneRouteId { get; set; }
    }
    [ProtoContract]
    public sealed class I_LinkRoamingResponse : AMessage, IRouteResponse
    {
        public uint OpCode() { return Fantasy.Network.OpCode.LinkRoamingResponse; }
        [ProtoMember(1)]
        public long TerminusId { get; set; }
        [ProtoMember(2)]
        public uint ErrorCode { get; set; }
    }
    [ProtoContract]
    public sealed class I_UnLinkRoamingRequest : AMessage, IRouteRequest
    {
        [ProtoIgnore]
        public I_UnLinkRoamingResponse ResponseType { get; set; }
        public uint OpCode() { return Fantasy.Network.OpCode.UnLinkRoamingRequest; }
        public long RouteTypeOpCode() { return 1; }
        [ProtoMember(1)]
        public long RoamingId { get; set; }
        [ProtoMember(2)]
        public bool DisposeRoaming { get; set; }
    }
    [ProtoContract]
    public sealed class I_UnLinkRoamingResponse : AMessage, IRouteResponse
    {
        public uint OpCode() { return Fantasy.Network.OpCode.UnLinkRoamingResponse; }
        [ProtoMember(1)]
        public uint ErrorCode { get; set; }
    }
    [ProtoContract]
    public partial class I_LockTerminusIdRequest : AMessage, IRouteRequest
    {
        [ProtoIgnore]
        public I_LockTerminusIdResponse ResponseType { get; set; }
        public uint OpCode() { return Fantasy.Network.OpCode.LockTerminusIdRequest; }
        [ProtoMember(1)]
        public long SessionRuntimeId { get; set; }
        [ProtoMember(2)]
        public int RoamingType { get; set; }
    }
    [ProtoContract]
    public partial class I_LockTerminusIdResponse : AMessage, IRouteResponse
    {
        public uint OpCode() { return Fantasy.Network.OpCode.LockTerminusIdResponse; }
        [ProtoMember(1)]
        public uint ErrorCode { get; set; }
    }
    [ProtoContract]
    public sealed class I_UnLockTerminusIdRequest : AMessage, IRouteRequest
    {
        [ProtoIgnore]
        public I_UnLockTerminusIdResponse ResponseType { get; set; }
        public uint OpCode() { return Fantasy.Network.OpCode.UnLockTerminusIdRequest; }
        public long RouteTypeOpCode() { return 1; }
        [ProtoMember(1)]
        public long SessionRuntimeId { get; set; }
        [ProtoMember(2)]
        public int RoamingType { get; set; }
        [ProtoMember(3)]
        public long TerminusId { get; set; }
        [ProtoMember(4)]
        public long TargetSceneRouteId { get; set; }
    }
    [ProtoContract]
    public sealed class I_UnLockTerminusIdResponse : AMessage, IRouteResponse
    {
        public uint OpCode() { return Fantasy.Network.OpCode.UnLockTerminusIdResponse; }
        [ProtoMember(1)]
        public uint ErrorCode { get; set; }
    }
    /// <summary>
    ///  漫游传送终端的请求
    /// </summary>
    public partial class I_TransferTerminusRequest : AMessage, IRouteRequest
    {
        [BsonIgnore]
        public I_TransferTerminusResponse ResponseType { get; set; }
        public uint OpCode() { return Fantasy.Network.OpCode.TransferTerminusRequest; }
        public Terminus Terminus { get; set; }
    }
    public partial class I_TransferTerminusResponse : AMessage, IRouteResponse
    {
        public uint OpCode() { return Fantasy.Network.OpCode.TransferTerminusResponse; }
        public uint ErrorCode { get; set; }
    }
    /// <summary>
    /// 用于服务器之间获取漫游的TerminusId。
    /// </summary>
    [ProtoContract]
    public partial class I_GetTerminusIdRequest : AMessage, IRouteRequest
    {
        [ProtoIgnore]
        public I_GetTerminusIdResponse ResponseType { get; set; }
        public uint OpCode() { return Fantasy.Network.OpCode.GetTerminusIdRequest; }
        [ProtoMember(1)]
        public int RoamingType { get; set; }
        [ProtoMember(2)]
        public long SessionRuntimeId { get; set; }
    }
    [ProtoContract]
    public partial class I_GetTerminusIdResponse : AMessage, IRouteResponse
    {
        public uint OpCode() { return Fantasy.Network.OpCode.GetTerminusIdResponse; }
        [ProtoMember(1)]
        public long TerminusId { get; set; }
        [ProtoMember(2)]
        public uint ErrorCode { get; set; }
    }
#endif
}

框架约定的协同落地 :所有消息类均采用 "继承AMessage+ 实现IMessage派生接口" 的组合模式,实现了规范约束与功能能力的无缝融合。通过继承AMessage,自动获得序列化适配、对象池管理、场景关联等基础功能;通过实现IRequest/IRouteResponse等接口,明确消息的通信场景(如请求 / 响应、跨节点路由),并通过OpCode()方法返回唯一标识(如BenchmarkMessage返回OpCode.BenchmarkMessage)。

序列化与通信适配 :通过[ProtoContract]类注解与[ProtoMember(N)]字段注解,直接适配 ProtoBuf 序列化框架,确保消息可被高效序列化为网络传输格式。同时,对无需序列化的字段(如ResponseType,用于编译时类型绑定)标记[ProtoIgnore],避免冗余数据传输。例如PingRequestRpcId通过[ProtoMember(1)]指定序列化顺序,既满足网络传输需求,又简化了序列化逻辑的实现。

请求 - 响应的标准化配对 :请求类消息(如BenchmarkRequestPingRequest)通过ResponseType字段与对应响应类(如BenchmarkResponsePingResponse)绑定,配合RpcId字段实现请求与响应的精准匹配。让框架能自动将响应关联到发起的请求,确保分布式环境下通信的可靠性 ------ 例如PingRequestRpcId会在PingResponse中带回,用于定位对应的请求回调。

分布式场景的精准适配:针对不同分布式通信需求,消息类通过字段设计与接口实现实现场景化适配:

  • 基础通信场景:PingRequest/PingResponse通过Now字段传递时间戳,支持节点心跳检测;
  • 地址管理场景:I_AddressableAdd_Request等通过AddressableIdRouteId,实现分布式对象的跨节点定位与管理;
  • 漫游场景:I_LinkRoamingRequest/I_UnLinkRoamingRequest通过RoamingIdTerminusId等字段,支持对象在节点间迁移时的消息跟随与链路维护。

消息注册与 OpCode 映射:从类型到标识的绑定

消息类型需与 OpCode(协议编号)绑定,才能被框架识别并参与序列化和分发。框架通过特性标记、反射扫描、缓存映射三重机制,实现 OpCode 的自动化管理与高效查询。MessageDispatcherComponent 负责管理这种映射关系,其核心代码位于 Runtime/Core/Network/Message/MessageDispatcherComponent.cs

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Fantasy.Assembly;
using Fantasy.Async;
using Fantasy.DataStructure.Collection;
using Fantasy.DataStructure.Dictionary;
using Fantasy.Entitas;
using Fantasy.InnerMessage;
using Fantasy.Network;
#pragma warning disable CS8604 // Possible null reference argument.

#pragma warning disable CS8602 // Dereference of a possibly null reference.
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.

#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
namespace Fantasy.Network.Interface
{
    /// <summary>
    /// 用于存储消息处理器的信息,包括类型和对象实例。
    /// </summary>
    /// <typeparam name="T">消息处理器的类型</typeparam>
    internal sealed class HandlerInfo<T>
    {
        /// <summary>
        /// 获取或设置消息处理器对象。
        /// </summary>
        public T Obj;
        /// <summary>
        /// 获取或设置消息处理器的类型。
        /// </summary>
        public Type Type;
    }
    
    /// <summary>
    /// 网络消息分发组件。
    /// </summary>
    public sealed class MessageDispatcherComponent : Entity, IAssembly
    {
        public long AssemblyIdentity { get; set; }
        private readonly Dictionary<Type, Type> _responseTypes = new Dictionary<Type, Type>();
        private readonly DoubleMapDictionary<uint, Type> _networkProtocols = new DoubleMapDictionary<uint, Type>();
        private readonly Dictionary<Type, IMessageHandler> _messageHandlers = new Dictionary<Type, IMessageHandler>();
        private readonly OneToManyList<long, Type> _assemblyResponseTypes = new OneToManyList<long, Type>();
        private readonly OneToManyList<long, uint> _assemblyNetworkProtocols = new OneToManyList<long, uint>();
        private readonly OneToManyList<long, HandlerInfo<IMessageHandler>> _assemblyMessageHandlers = new OneToManyList<long, HandlerInfo<IMessageHandler>>();
#if FANTASY_UNITY
        private readonly Dictionary<Type, IMessageDelegateHandler> _messageDelegateHandlers = new Dictionary<Type, IMessageDelegateHandler>();
#endif
#if FANTASY_NET
        private readonly Dictionary<long, int> _customRouteMap = new Dictionary<long, int>();
        private readonly OneToManyList<long, long> _assemblyCustomRouteMap = new OneToManyList<long, long>();
        private readonly Dictionary<Type, IRouteMessageHandler> _routeMessageHandlers = new Dictionary<Type, IRouteMessageHandler>();
        private readonly OneToManyList<long, HandlerInfo<IRouteMessageHandler>> _assemblyRouteMessageHandlers = new OneToManyList<long, HandlerInfo<IRouteMessageHandler>>();
#endif
        private CoroutineLock _receiveRouteMessageLock;
        
        #region Initialize

        internal async FTask<MessageDispatcherComponent> Initialize()
        {
            _receiveRouteMessageLock = Scene.CoroutineLockComponent.Create(GetType().TypeHandle.Value.ToInt64());
            await AssemblySystem.Register(this);
            return this;
        }

        public async FTask Load(long assemblyIdentity)
        {
            var tcs = FTask.Create(false);
            Scene?.ThreadSynchronizationContext.Post(() =>
            {
                LoadInner(assemblyIdentity);
                tcs.SetResult();
            });
            await tcs;
        }

        private void LoadInner(long assemblyIdentity)
        {
            // 遍历所有实现了IMessage接口的类型,获取OpCode并添加到_networkProtocols字典中
            foreach (var type in AssemblySystem.ForEach(assemblyIdentity, typeof(IMessage)))
            {
                var obj = (IMessage) Activator.CreateInstance(type);
                var opCode = obj.OpCode();
                
                _networkProtocols.Add(opCode, type);
                
                var responseType = type.GetProperty("ResponseType");
                
                // 如果类型具有ResponseType属性,将其添加到_responseTypes字典中
                if (responseType != null)
                {
                    _responseTypes.Add(type, responseType.PropertyType);
                    _assemblyResponseTypes.Add(assemblyIdentity, type);
                }
                
                _assemblyNetworkProtocols.Add(assemblyIdentity, opCode);
            }
            
            // 遍历所有实现了IMessageHandler接口的类型,创建实例并添加到_messageHandlers字典中
            foreach (var type in AssemblySystem.ForEach(assemblyIdentity, typeof(IMessageHandler)))
            {
                var obj = (IMessageHandler) Activator.CreateInstance(type);

                if (obj == null)
                {
                    throw new Exception($"message handle {type.Name} is null");
                }

                var key = obj.Type();
                _messageHandlers.Add(key, obj);
                _assemblyMessageHandlers.Add(assemblyIdentity, new HandlerInfo<IMessageHandler>()
                {
                    Obj = obj, Type = key
                });
            }
            
            // 如果编译符号FANTASY_NET存在,遍历所有实现了IRouteMessageHandler接口的类型,创建实例并添加到_routeMessageHandlers字典中
#if FANTASY_NET
            foreach (var type in AssemblySystem.ForEach(assemblyIdentity, typeof(IRouteMessageHandler)))
            {
                var obj = (IRouteMessageHandler) Activator.CreateInstance(type);

                if (obj == null)
                {
                    throw new Exception($"message handle {type.Name} is null");
                }

                var key = obj.Type();
                _routeMessageHandlers.Add(key, obj);
                _assemblyRouteMessageHandlers.Add(assemblyIdentity, new HandlerInfo<IRouteMessageHandler>()
                {
                    Obj = obj, Type = key
                });
            }

            foreach (var type in AssemblySystem.ForEach(assemblyIdentity, typeof(ICustomRoute)))
            {
                var obj = (ICustomRoute) Activator.CreateInstance(type);
                
                if (obj == null)
                {
                    throw new Exception($"message handle {type.Name} is null");
                }

                var opCode = obj.OpCode();
                _customRouteMap[opCode] = obj.RouteType;
                _assemblyCustomRouteMap.Add(assemblyIdentity, opCode);
            }
#endif
        }

        public async FTask ReLoad(long assemblyIdentity)
        {
            var tcs = FTask.Create(false);
            Scene?.ThreadSynchronizationContext.Post(() =>
            {
                OnUnLoadInner(assemblyIdentity);
                LoadInner(assemblyIdentity);
                tcs.SetResult();
            });
            await tcs;
        }

        public async FTask OnUnLoad(long assemblyIdentity)
        {
            var tcs = FTask.Create(false);
            Scene?.ThreadSynchronizationContext.Post(() =>
            {
                OnUnLoadInner(assemblyIdentity);
                tcs.SetResult();
            });
            await tcs;
        }

        private void OnUnLoadInner(long assemblyIdentity)
        {
            // 移除程序集对应的ResponseType类型和OpCode信息
            if (_assemblyResponseTypes.TryGetValue(assemblyIdentity, out var removeResponseTypes))
            {
                foreach (var removeResponseType in removeResponseTypes)
                {
                    _responseTypes.Remove(removeResponseType);
                }

                _assemblyResponseTypes.RemoveByKey(assemblyIdentity);
            }

            if (_assemblyNetworkProtocols.TryGetValue(assemblyIdentity, out var removeNetworkProtocols))
            {
                foreach (var removeNetworkProtocol in removeNetworkProtocols)
                {
                    _networkProtocols.RemoveByKey(removeNetworkProtocol);
                }

                _assemblyNetworkProtocols.RemoveByKey(assemblyIdentity);
            }

            // 移除程序集对应的消息处理器信息
            if (_assemblyMessageHandlers.TryGetValue(assemblyIdentity, out var removeMessageHandlers))
            {
                foreach (var removeMessageHandler in removeMessageHandlers)
                {
                    _messageHandlers.Remove(removeMessageHandler.Type);
                }

                _assemblyMessageHandlers.RemoveByKey(assemblyIdentity);
            }

            // 如果编译符号FANTASY_NET存在,移除程序集对应的路由消息处理器信息
#if FANTASY_NET
            if (_assemblyRouteMessageHandlers.TryGetValue(assemblyIdentity, out var removeRouteMessageHandlers))
            {
                foreach (var removeRouteMessageHandler in removeRouteMessageHandlers)
                {
                    _routeMessageHandlers.Remove(removeRouteMessageHandler.Type);
                }

                _assemblyRouteMessageHandlers.RemoveByKey(assemblyIdentity);
            }

            if (_assemblyCustomRouteMap.TryGetValue(assemblyIdentity, out var removeCustomRouteMap))
            {
                foreach (var removeCustom in removeCustomRouteMap)
                {
                    _customRouteMap.Remove(removeCustom);
                }

                _assemblyCustomRouteMap.RemoveByKey(assemblyIdentity);
            }
#endif
        }
        
#if FANTASY_UNITY       
        /// <summary>
        /// 手动注册一个消息处理器。
        /// </summary>
        /// <param name="delegate"></param>
        /// <typeparam name="T"></typeparam>
        public void RegisterHandler<T>(MessageDelegate<T> @delegate) where T : IMessage
        {
            var type = typeof(T);

            if (!_messageDelegateHandlers.TryGetValue(type, out var messageDelegate))
            {
                messageDelegate = new MessageDelegateHandler<T>();
                _messageDelegateHandlers.Add(type,messageDelegate);
            }

            messageDelegate.Register(@delegate);
        }

        /// <summary>
        /// 手动卸载一个消息处理器,必须是通过RegisterHandler方法注册的消息处理器。
        /// </summary>
        /// <param name="delegate"></param>
        /// <typeparam name="T"></typeparam>
        public void UnRegisterHandler<T>(MessageDelegate<T> @delegate) where T : IMessage
        {
            var type = typeof(T);
            
            if (!_messageDelegateHandlers.TryGetValue(type, out var messageDelegate))
            {
                return;
            }

            if (messageDelegate.UnRegister(@delegate) != 0)
            {
                return;
            }
            
            _messageDelegateHandlers.Remove(type);
        }
#endif
        #endregion
        
        /// <summary>
        /// 处理普通消息,将消息分发给相应的消息处理器。
        /// </summary>
        /// <param name="session">会话对象</param>
        /// <param name="type">消息类型</param>
        /// <param name="message">消息对象</param>
        /// <param name="rpcId">RPC标识</param>
        /// <param name="protocolCode">协议码</param>
        public void MessageHandler(Session session, Type type, object message, uint rpcId, uint protocolCode)
        {
#if FANTASY_UNITY
            if(_messageDelegateHandlers.TryGetValue(type,out var messageDelegateHandler))
            {
                messageDelegateHandler.Handle(session, message);
                return;
            }
#endif
            if (!_messageHandlers.TryGetValue(type, out var messageHandler))
            {
                Log.Warning($"Scene:{session.Scene.Id} Found Unhandled Message: {message.GetType()}");
                return;
            }
            
            // 调用消息处理器的Handle方法并启动协程执行处理逻辑
            messageHandler.Handle(session, rpcId, protocolCode, message).Coroutine();
        }
        
        // 如果编译符号FANTASY_NET存在,定义处理路由消息的方法
#if FANTASY_NET
        /// <summary>
        /// 处理路由消息,将消息分发给相应的路由消息处理器。
        /// </summary>
        /// <param name="session">会话对象</param>
        /// <param name="type">消息类型</param>
        /// <param name="entity">实体对象</param>
        /// <param name="message">消息对象</param>
        /// <param name="rpcId">RPC标识</param>
        public async FTask RouteMessageHandler(Session session, Type type, Entity entity, object message, uint rpcId)
        {
            if (!_routeMessageHandlers.TryGetValue(type, out var routeMessageHandler))
            {
                Log.Warning($"Scene:{session.Scene.Id} Found Unhandled RouteMessage: {message.GetType()}");

                if (message is IRouteRequest request)
                {
                    FailRouteResponse(session, request.GetType(), InnerErrorCode.ErrEntityNotFound, rpcId);
                }

                return;
            }
            
            var runtimeId = entity.RuntimeId;
            var sessionRuntimeId = session.RuntimeId;

            if (entity is Scene)
            {
                // 如果是Scene的话、就不要加锁了、如果加锁很一不小心就可能会造成死锁
                await routeMessageHandler.Handle(session, entity, rpcId, message);
                return;
            }
            
            // 使用协程锁来确保多线程安全
            using (await _receiveRouteMessageLock.Wait(runtimeId))
            {
                if (sessionRuntimeId != session.RuntimeId)
                {
                    return;
                }
                
                if (runtimeId != entity.RuntimeId)
                {
                    if (message is IRouteRequest request)
                    {
                        FailRouteResponse(session, request.GetType(), InnerErrorCode.ErrEntityNotFound, rpcId);
                    }
                
                    return;
                }

                await routeMessageHandler.Handle(session, entity, rpcId, message);
            }
        }

        internal bool GetCustomRouteType(long protocolCode, out int routeType)
        {
            return _customRouteMap.TryGetValue(protocolCode, out routeType);
        }
#endif
        internal void FailRouteResponse(Session session, Type requestType, uint error, uint rpcId)
        {
            var response = CreateRouteResponse(requestType, error);
            session.Send(response, rpcId);
        }
        
        internal IResponse CreateResponse(Type requestType, uint error)
        {
            IResponse response;

            if (_responseTypes.TryGetValue(requestType, out var responseType))
            {
                response = (IResponse) Activator.CreateInstance(responseType);
            }
            else
            {
                response = new Response();
            }

            response.ErrorCode = error;
            return response;
        }
        
        internal IRouteResponse CreateRouteResponse(Type requestType, uint error)
        {
            IRouteResponse response;

            if (_responseTypes.TryGetValue(requestType, out var responseType))
            {
                response = (IRouteResponse) Activator.CreateInstance(responseType);
            }
            else
            {
                response = new RouteResponse();
            }

            response.ErrorCode = error;
            return response;
        }
        
        /// <summary>
        /// 根据消息类型获取对应的OpCode。
        /// </summary>
        /// <param name="type">消息类型</param>
        /// <returns>消息对应的OpCode</returns>
        public uint GetOpCode(Type type)
        {
            return _networkProtocols.GetKeyByValue(type);
        }

        /// <summary>
        /// 根据OpCode获取对应的消息类型。
        /// </summary>
        /// <param name="code">OpCode</param>
        /// <returns>OpCode对应的消息类型</returns>
        public Type GetOpCodeType(uint code)
        {
            return _networkProtocols.GetValueByKey(code);
        }
    }
}

MessageDispatcherComponent:消息映射与分发的核心中枢

MessageDispatcherComponent 是框架中管理 "消息类型 - OpCode - 处理器" 映射关系的核心组件,通过动态装配、双向查询、多环境适配等机制,实现了从网络字节流到业务逻辑的完整流转。

核心只读字段:映射关系的存储载体

所有核心字段均为 private readonly,确保容器实例不可替换,仅允许通过内部方法修改内容,避免意外替换导致的映射错乱,同时为线程安全提供基础。

核心映射字典:支撑类型与标识的绑定

  • Dictionary<Type, Type> _responseTypes :存储 "请求类型→响应类型" 的映射(如 typeof(PingRequest) → typeof(PingResponse))。程序集加载时,通过解析消息类型的 ResponseType 属性填充,为 CreateResponse 方法提供依据 ------ 生成响应时,框架可直接通过请求类型查询对应的响应类型,反射创建实例并设置错误码,简化请求 - 响应模式实现。
  • DoubleMapDictionary<uint, Type> _networkProtocols :双向映射的核心载体,同时维护 "OpCode→消息类型" 与 "消息类型→OpCode" 的关联(如 1001 ↔ typeof(PingRequest))。通过 Add/Remove 方法修改内部映射,确保双向查询一致性,是序列化器 "字节流→对象" 转换的关键依据(接收端通过 OpCode 查类型)与消息发送的标识来源(发送端通过类型查 OpCode)。
  • Dictionary<Type, IMessageHandler> _messageHandlers :存储 "消息类型→处理器" 的映射(如 typeof(PingRequest) → PingHandler 实例)。程序集加载时,通过 IMessageHandler.Type() 关联处理器与消息类型,消息反序列化后,框架通过该字典快速定位处理器并执行逻辑,是 "数据→业务" 的直接连接器。

程序集关联列表:支撑热更新的批量操作

  • OneToManyList<long, Type> _assemblyResponseTypes :记录 "程序集标识→请求类型" 的一对多关联(如 "程序集 ID=100→PingRequestLoginRequest")。卸载程序集时,通过该列表批量获取请求类型,从 _responseTypes 中移除对应映射,确保旧响应关系被完整清理。
  • OneToManyList<long, uint> _assemblyNetworkProtocols :记录 "程序集标识→OpCode" 的关联(如 "程序集 ID=100→1001、1002")。卸载时,通过该列表批量获取 OpCode,从 _networkProtocols 中移除对应映射,避免残留无效的 OpCode - 类型关联。
  • OneToManyList<long, HandlerInfo<IMessageHandler>> _assemblyMessageHandlers :记录 "程序集标识→处理器关联信息"(含处理器实例与消息类型)。卸载时,通过该列表批量从 _messageHandlers 中移除映射,确保旧处理器实例被完全回收,避免内存泄漏。

环境特定字段:适配客户端与服务端差异

  • (Unity 客户端)Dictionary<Type, IMessageDelegateHandler> _messageDelegateHandlers :存储 "消息类型→委托处理器" 的映射,封装客户端通过 RegisterHandler 注册的临时委托(如 UI 面板的消息响应)。优先于常规处理器执行,适配客户端事件驱动场景,且支持动态注销避免内存泄漏。
  • (服务端)Dictionary<long, int> _customRouteMapOneToManyList<long, long> _assemblyCustomRouteMap :前者存储 "OpCode→路由类型" 的映射(如 1003 → 2),支撑跨节点消息的自定义路由策略;后者记录 "程序集标识→路由 OpCode" 的关联,用于卸载时批量清理旧路由规则。
  • (服务端)Dictionary<Type, IRouteMessageHandler> _routeMessageHandlersOneToManyList<long, HandlerInfo<IRouteMessageHandler>> _assemblyRouteMessageHandlers :前者存储 "路由消息类型→路由处理器" 的映射(如 typeof(CrossServerChat) → CrossServerChatHandler),专门处理跨节点交互;后者记录程序集关联,用于卸载时批量清理路由处理器映射。

线程安全字段:保障分布式并发安全 :- CoroutineLock _receiveRouteMessageLock :服务端路由消息的协程锁,通过 Scene.CoroutineLockComponent.Create 初始化,以组件类型哈希值为锁键。确保同一实体的路由消息(如玩家跨服操作)通过 _receiveRouteMessageLock.Wait(runtimeId) 串行执行,避免并发修改实体状态导致的数据不一致。

核心方法:映射关系的动态管理流程

Initialize:组件启动入口 :初始化路由消息的协程锁(_receiveRouteMessageLock),为服务端线程安全处理提供基础;将组件注册到 AssemblySystem,使其能被框架感知并在程序集加载时自动触发 Load 方法,完成初始映射注册。 LoadLoadInner:映射关系的创建

  • Load :负责线程调度,将加载逻辑投递到组件所属 Scene 的逻辑线程执行,避免多线程并发修改字段导致的错乱。
  • LoadInner :核心逻辑实现,通过扫描程序集中的 IMessageIMessageHandler 等类型,反射创建实例并提取关键信息(如 OpCodeType()),填充 _networkProtocols_messageHandlers 等核心字段,同时通过 OneToManyList 记录程序集关联,为后续卸载做准备。 OnUnLoadOnUnLoadInner:映射关系的清理
  • OnUnLoad:负责线程调度,将卸载逻辑投递到逻辑线程执行,确保线程安全。
  • OnUnLoadInner :核心清理逻辑,依赖 OneToManyList 字段批量定位该程序集注册的映射关系,从 _responseTypes_networkProtocols_messageHandlers 等字段中移除对应条目,最后清空 OneToManyList 中的关联记录,避免残留。 ReLoad:热更新的 "先清后加" :实现消息逻辑的热更新:先通过 OnUnLoadInner 清理该程序集的旧映射,再通过 LoadInner 加载新程序集的内容,确保旧协议、旧处理器被完全替换,新逻辑无缝生效(如活动玩法协议升级)。 消息处理与分发:字段与方法的协同
  • 普通消息处理(MessageHandler :反序列化后,通过消息类型从 _messageHandlers 查找处理器并调用 Handle 方法;Unity 端优先使用 _messageDelegateHandlers 中的委托处理器,适配 UI 交互。
  • 路由消息处理(RouteMessageHandler :服务端通过 _routeMessageHandlers 查找路由处理器,结合 _receiveRouteMessageLock 确保线程安全;若处理器不存在,自动返回 ErrEntityNotFound 错误响应。
  • 响应对象创建(CreateResponse/CreateRouteResponse :根据请求类型从 _responseTypes 查找响应类型,反射创建实例并填充错误码;若未找到,自动降级使用默认响应对象(如 Response),简化请求 - 响应实现。

序列化机制:跨节点数据转换的核心引擎

序列化机制是消息在网络中传输的 "翻译器",Fantasy 框架通过接口抽象与多实现,支持不同序列化协议(如 ProtoBufBson),并通过管理器实现灵活调度。

ISerialize:序列化逻辑的「宪法性文件」

ISerialize 是连接 "字节流" 与 "业务对象" 的核心接口,它定义了数据序列化(业务对象→字节流)与反序列化(字节流→业务对象)的标准契约。解析器的 Pack(打包)和 UnPack(解包)方法均通过该接口实现与具体序列化协议(如 ProtobufBson 等)无关的数据转换,是框架支持多协议适配的关键组件。其源码位于 Runtime/Core/Serialization/ISerialize.cs

csharp 复制代码
using System;
using System.Buffers;

#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
namespace Fantasy.Serialize
{
    public interface ISerialize
    {
        /// <summary>
        /// 序列化器的名字,用于在协议里指定用什么协议序列化使用
        /// </summary>
        string SerializeName { get; }
        /// <summary>
        /// 反序列化
        /// </summary>
        /// <param name="bytes"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        T Deserialize<T>(byte[] bytes);
        /// <summary>
        /// 反序列化
        /// </summary>
        /// <param name="buffer"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        T Deserialize<T>(MemoryStreamBuffer buffer);
        /// <summary>
        /// 反序列化
        /// </summary>
        /// <param name="type"></param>
        /// <param name="bytes"></param>
        /// <returns></returns>
        object Deserialize(Type type, byte[] bytes);
        /// <summary>
        /// 反序列化
        /// </summary>
        /// <param name="type"></param>
        /// <param name="buffer"></param>
        /// <returns></returns>
        object Deserialize(Type type, MemoryStreamBuffer buffer);
        /// <summary>
        /// 反序列化
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="index"></param>
        /// <param name="count"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        T Deserialize<T>(byte[] bytes, int index, int count);
        /// <summary>
        /// 反序列化
        /// </summary>
        /// <param name="type"></param>
        /// <param name="bytes"></param>
        /// <param name="index"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        object Deserialize(Type type, byte[] bytes, int index, int count);
        /// <summary>
        /// 序列化
        /// </summary>
        /// <param name="object"></param>
        /// <param name="buffer"></param>
        /// <typeparam name="T"></typeparam>
        void Serialize<T>(T @object, IBufferWriter<byte> buffer);
        /// <summary>
        /// 序列化
        /// </summary>
        /// <param name="object"></param>
        /// <param name="buffer"></param>
        void Serialize(object @object, IBufferWriter<byte> buffer);
        /// <summary>
        /// 序列化
        /// </summary>
        /// <param name="type"></param>
        /// <param name="object"></param>
        /// <param name="buffer"></param>
        void Serialize(Type type, object @object, IBufferWriter<byte> buffer);
        /// <summary>
        /// 克隆
        /// </summary>
        /// <param name="t"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        T Clone<T>(T t);
    }
}
接口定义的核心契约:从功能约定到协作逻辑

序列化器标识机制 :通过 SerializeName 属性定义序列化器的唯一名称(如 ProtobufBson),作为协议中指定序列化方式的标识依据。框架可通过该名称动态匹配对应的序列化器,实现多协议场景下的灵活切换(例如根据消息头中的协议类型字段,选择对应 SerializeName 的实现类)。

反序列化方法的多场景适配 :定义 6 个 Deserialize 重载方法,覆盖不同输入源与使用场景:

  • 支持从基础字节数组(byte[])、内存流缓冲区(MemoryStreamBuffer)读取数据,适配原始字节与封装流两种数据载体;
  • 提供泛型(T)与非泛型(Type)接口,满足编译期类型明确(如 Deserialize<T>)和运行期动态类型(如 Deserialize(Type type, ...))的需求;
  • 允许指定字节数组的起始索引(index)与长度(count),支持从大缓冲区中截取片段解析,避免额外数据拷贝(如直接解析粘包中的某段消息体)。

序列化方法的高效缓冲设计 :定义 3 个 Serialize 重载方法,统一采用 IBufferWriter<byte> 作为输出缓冲区接口:

  • 支持泛型对象(T)、任意对象(object)与指定类型(Type)的序列化,覆盖强类型与动态类型场景;
  • 基于 IBufferWriter<byte> 的缓冲写入能力(如自动扩容、减少内存分配),适配网络传输中高效写入字节流的需求(例如直接向内存池租赁的缓冲区写入序列化结果,避免中间数组转换)。

对象克隆能力 :通过 Clone<T> 方法提供基于序列化 - 反序列化的对象深拷贝能力,利用自身的序列化与反序列化逻辑,生成独立于原对象的副本,保证复杂对象(如嵌套结构)克隆后的独立性,适配业务中需隔离对象状态的场景(如消息转发时的对象复用)。

ISerialize 的设计核心是「通用契约 + 最小约束」,为 ProtobufBson 等不同协议的实现预留了足够的扩展空间,同时保证上层逻辑能通过统一接口调用任意序列化器。

具体序列化器实现:从 ProtoBuf 到 Bson 的多协议适配

框架针对不同场景提供了两种核心序列化器:ProtoBufSerialize(高效紧凑,适合网络传输)和 BsonSerialize(支持复杂类型,适合数据存储与跨语言交互)。

Protobuf 序列化器:高性能二进制协议的实现

ProtobufGoogle 推出的二进制序列化协议,具有体积小、解析快的特点,是分布式网络通信的理想选择。Fantasy 框架针对 .NET 与 Unity 环境分别提供了实现:ProtobufPackHelperNetProtobufPackHelperUnity。均遵循 ISerialize 接口规范。

Net 平台:ProtoBufPackHelperNet 实现

源码位于 Runtime/Core/Serialize/ProtoBufPackHelper/ProtoBufPackHelperNet.cs

less 复制代码
#if FANTASY_NET || FANTASY_EXPORTER
using System.Buffers;
using Fantasy.Assembly;
using ProtoBuf.Meta;
namespace Fantasy.Serialize
{
    /// <summary>
    /// ProtoBufP帮助类,Net平台使用
    /// </summary>
    public sealed class ProtoBufPackHelper : ISerialize
    {
        /// <summary>
        /// 序列化器的名字
        /// </summary>
        public string SerializeName { get; } = "ProtoBuf";
        
        /// <summary>
        /// 构造函数
        /// </summary>
        public ProtoBufPackHelper ()
        {
#if FANTASY_NET
            RuntimeTypeModel.Default.AutoAddMissingTypes = true;
            RuntimeTypeModel.Default.AllowParseableTypes = true;
            RuntimeTypeModel.Default.AutoAddMissingTypes = true;
            RuntimeTypeModel.Default.AutoCompile = true;
            RuntimeTypeModel.Default.UseImplicitZeroDefaults = true;
            RuntimeTypeModel.Default.InferTagFromNameDefault = true;
            
            foreach (var type in AssemblySystem.ForEach(typeof(IProto)))
            {
                RuntimeTypeModel.Default.Add(type, true);
            }

            RuntimeTypeModel.Default.CompileInPlace();
#endif
        }

        /// <summary>
        /// 使用ProtoBuf反序列化数据到实例
        /// </summary>
        /// <param name="bytes"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T Deserialize<T>(byte[] bytes)
        {
            var memory = new ReadOnlyMemory<byte>(bytes);
            var @object = RuntimeTypeModel.Default.Deserialize<T>(memory);
            if (@object is ASerialize aSerialize)
            {
                aSerialize.AfterDeserialization();
            }
            return @object;
        }
        /// <summary>
        /// 使用ProtoBuf反序列化数据到实例
        /// </summary>
        /// <param name="buffer"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T Deserialize<T>(MemoryStreamBuffer buffer)
        {
            var @object = RuntimeTypeModel.Default.Deserialize<T>(buffer);
            if (@object is ASerialize aSerialize)
            {
                aSerialize.AfterDeserialization();
            }

            return @object;
        }
        /// <summary>
        /// 使用ProtoBuf反序列化数据到实例
        /// </summary>
        /// <param name="type"></param>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public object Deserialize(Type type, byte[] bytes)
        {
            var memory = new ReadOnlyMemory<byte>(bytes);
            var @object = RuntimeTypeModel.Default.Deserialize(type, memory);
            if (@object is ASerialize aSerialize)
            {
                aSerialize.AfterDeserialization();
            }
            return @object;
        }
        /// <summary>
        /// 使用ProtoBuf反序列化数据到实例
        /// </summary>
        /// <param name="type"></param>
        /// <param name="buffer"></param>
        /// <returns></returns>
        public object Deserialize(Type type, MemoryStreamBuffer buffer)
        {
            var @object = RuntimeTypeModel.Default.Deserialize(type, buffer);
            if (@object is ASerialize aSerialize)
            {
                aSerialize.AfterDeserialization();
            }

            return @object;
        }
        /// <summary>
        /// 使用ProtoBuf反序列化数据到实例
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="index"></param>
        /// <param name="count"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T Deserialize<T>(byte[] bytes, int index, int count)
        {
            var memory = new ReadOnlyMemory<byte>(bytes, index, count);
            var @object = RuntimeTypeModel.Default.Deserialize<T>(memory);
            if (@object is ASerialize aSerialize)
            {
                aSerialize.AfterDeserialization();
            }

            return @object;
        }
        /// <summary>
        /// 使用ProtoBuf反序列化数据到实例
        /// </summary>
        /// <param name="type"></param>
        /// <param name="bytes"></param>
        /// <param name="index"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public object Deserialize(Type type, byte[] bytes, int index, int count)
        {
            var memory = new ReadOnlyMemory<byte>(bytes, index, count);
            var @object = RuntimeTypeModel.Default.Deserialize(type, memory);
            if (@object is ASerialize aSerialize)
            {
                aSerialize.AfterDeserialization();
            }

            return @object;
        }
        /// <summary>
        /// 使用ProtoBuf序列化某一个实例到IBufferWriter中
        /// </summary>
        /// <param name="object"></param>
        /// <param name="buffer"></param>
        /// <typeparam name="T"></typeparam>
        public void Serialize<T>(T @object, IBufferWriter<byte> buffer)
        {
            if (@object is ASerialize aSerialize)
            {
                aSerialize.BeginInit();
            }

            RuntimeTypeModel.Default.Serialize<T>(buffer, @object);
        }
        /// <summary>
        /// 使用ProtoBuf序列化某一个实例到IBufferWriter中
        /// </summary>
        /// <param name="object"></param>
        /// <param name="buffer"></param>
        public void Serialize(object @object, IBufferWriter<byte> buffer)
        {
            if (@object is ASerialize aSerialize)
            {
                aSerialize.BeginInit();
            }

            RuntimeTypeModel.Default.Serialize(buffer, @object);
        }
        /// <summary>
        /// 使用ProtoBuf序列化某一个实例到IBufferWriter中
        /// </summary>
        /// <param name="type"></param>
        /// <param name="object"></param>
        /// <param name="buffer"></param>
        public void Serialize(Type type, object @object, IBufferWriter<byte> buffer)
        {
            if (@object is ASerialize aSerialize)
            {
                aSerialize.BeginInit();
            }

            RuntimeTypeModel.Default.Serialize(buffer, @object);
        }
        internal byte[] Serialize(object @object)
        {
            if (@object is ASerialize aSerialize)
            {
                aSerialize.BeginInit();
            }

            using (var buffer = new MemoryStream())
            {
                RuntimeTypeModel.Default.Serialize(buffer, @object);
                return buffer.ToArray();
            }
        }
        private byte[] Serialize<T>(T @object)
        {
            if (@object is ASerialize aSerialize)
            {
                aSerialize.BeginInit();
            }

            using (var buffer = new MemoryStream())
            {
                RuntimeTypeModel.Default.Serialize<T>(buffer, @object);
                return buffer.ToArray();
            }
        }
        /// <summary>
        /// 克隆
        /// </summary>
        /// <param name="t"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T Clone<T>(T t)
        {
            return Deserialize<T>(Serialize(t));
        }
    }
}
#endif
ProtoBufPackHelperNet 类设计:Protobuf 协议的序列化实现

核心定位 :作为 ISerialize 接口的具体实现类,专为 Net 平台提供 Protobuf 协议的序列化与反序列化能力,通过封装 Protobuf 官方 RuntimeTypeModel 实现 "业务对象↔字节流" 的高效转换,是框架多协议适配体系中 Protobuf 协议的核心载体。

核心标识与接口契约

  • 序列化器标识 :通过 SerializeName 属性固定标识为 ProtoBuf,作为框架动态匹配序列化器的依据(如解析器根据消息协议字段选择对应 SerializeName 的实现类),确保多协议场景下的精准路由。
  • 接口实现 :严格遵循 ISerialize 接口契约,实现全部 6 个 Deserialize 重载、3 个 Serialize 重载及 Clone 方法,覆盖泛型 / 非泛型、不同输入输出载体的序列化场景,保证与解析器(如 InnerBufferPacketParser)的 Pack/UnPack 方法无缝协作。

初始化配置与类型注册 :构造函数中完成 Protobuf 运行时模型(RuntimeTypeModel.Default)的核心配置与类型注册与编译优化,为序列化提供高性能基础环境:

  • 模型参数配置 :启用自动添加缺失类型(AutoAddMissingTypes = true)以适配动态类型场景;允许可解析类型(AllowParseableTypes = true)扩展支持范围;开启自动编译(AutoCompile = true)和就地编译(CompileInPlace())提升运行时序列化效率;通过 UseImplicitZeroDefaults = trueInferTagFromNameDefault = true 简化 Protobuf 标签定义(默认以字段名推断标签,零值字段隐式处理)。
  • 类型注册 :遍历所有实现 IProto 接口的类型(通过 AssemblySystem.ForEach(typeof(IProto)) 扫描,其中 IProto 作为空接口,其核心作用是标记接口 ),调用 RuntimeTypeModel.Default.Add(type, true) 将类型注册到模型中,确保这些业务类型可被 Protobuf 识别并正确序列化,为后续编解码提供类型支持。

反序列化逻辑 :提供 6 个 Deserialize 重载方法,覆盖多场景字节流解析需求,并结合 ASerialize 实现反序列化后处理:

  • 输入载体适配 :支持从 byte[](封装为 ReadOnlyMemory<byte> 实现零复制访问)、MemoryStreamBuffer(直接复用流接口)解析,支持通过 indexcount 参数截取缓冲区片段解析(适配粘包 / 分包场景)。
  • 类型支持 :提供泛型(Deserialize<T>)与非泛型(Deserialize(Type type, ...))方法,分别适配编译期类型明确与运行时动态类型场景。
  • 后处理机制 :若反序列化结果为 ASerialize 类型(框架序列化基类),自动调用 AfterDeserialization() 方法,执行状态恢复(如时间戳转 DateTime)、派生字段初始化、业务校验等反序列化后逻辑。

序列化逻辑 :实现 3 个 Serialize 重载方法,基于 IBufferWriter<byte> 实现高效写入,并结合 ASerialize 完成序列化前预处理:

  • 输出载体优化 :统一采用 IBufferWriter<byte> 作为输出接口,利用其自动扩容、内存池复用特性,直接将序列化结果写入网络传输缓冲区(如 MemoryStreamBuffer),减少中间数据拷贝。
  • 类型支持 :提供泛型(Serialize<T>)、任意对象(Serialize(object))、指定类型(Serialize(Type type, object))重载,覆盖不同类型场景。
  • 预处理机制 :若序列化对象为 ASerialize 类型,先调用 BeginInit() 方法,执行状态冻结(防止并发修改)、非序列化友好类型转换(如 DateTimeOffset 转时间戳)、临时数据清理等序列化前准备逻辑。
  • 内部辅助方法 :提供返回 byte[]Serialize 重载(用于 Clone 等场景),通过 MemoryStream 暂存结果并转换为数组。

克隆功能Clone<T> 方法通过 "序列化→反序列化" 链路实现对象深拷贝,先将原对象序列化为字节数组,再反序列化为新对象,确保克隆体与原对象完全独立(避免引用类型共享状态),适配消息转发等需隔离对象的场景。

Unity/Console 平台:ProtoBufPackHelperUnity 实现

其源码位于:Runtime/Core/Serialize/ProtoBufPackHelper/ProtoBufPackHelperUnity.cs

less 复制代码
#if FANTASY_UNITY || FANTASY_CONSOLE
using System;
using System.Buffers;
using System.IO;
using Fantasy.Assembly;
using ProtoBuf;
using ProtoBuf.Meta;

namespace Fantasy.Serialize
{
    /// <summary>
    /// ProtoBufP帮助类,Unity平台使用
    /// </summary>
    public sealed class ProtoBufPackHelper : ISerialize
    {
        /// <summary>
        /// 序列化器的名字
        /// </summary>
        public string SerializeName { get; } = "ProtoBuf";

        /// <summary>
        /// 使用ProtoBuf反序列化数据到实例
        /// </summary>
        /// <param name="bytes"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public unsafe T Deserialize<T>(byte[] bytes)
        {
            fixed (byte* ptr = bytes)
            {
                using var stream = new UnmanagedMemoryStream(ptr, bytes.Length);
                var @object = ProtoBuf.Serializer.Deserialize<T>(stream);
                if (@object is ASerialize aSerialize)
                {
                    aSerialize.AfterDeserialization();
                }
                return @object;
            }
        }
        /// <summary>
        /// 使用ProtoBuf反序列化数据到实例
        /// </summary>
        /// <param name="buffer"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T Deserialize<T>(MemoryStreamBuffer buffer)
        {
            var @object = ProtoBuf.Serializer.Deserialize<T>(buffer);
            if (@object is ASerialize aSerialize)
            {
                aSerialize.AfterDeserialization();
            }
            return @object;
        }
        /// <summary>
        /// 使用ProtoBuf反序列化数据到实例
        /// </summary>
        /// <param name="type"></param>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public unsafe object Deserialize(Type type, byte[] bytes)
        {
            fixed (byte* ptr = bytes)
            {
                using var stream = new UnmanagedMemoryStream(ptr, bytes.Length);
                var @object = ProtoBuf.Serializer.Deserialize(type, stream);
                if (@object is ASerialize aSerialize)
                {
                    aSerialize.AfterDeserialization();
                }

                return @object;
            }
        }
        /// <summary>
        /// 使用ProtoBuf反序列化数据到实例
        /// </summary>
        /// <param name="type"></param>
        /// <param name="buffer"></param>
        /// <returns></returns>
        public object Deserialize(Type type, MemoryStreamBuffer buffer)
        {
            var @object = ProtoBuf.Serializer.Deserialize(type, buffer);
            if (@object is ASerialize aSerialize)
            {
                aSerialize.AfterDeserialization();
            }

            return @object;
        }
        /// <summary>
        /// 使用ProtoBuf反序列化数据到实例
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="index"></param>
        /// <param name="count"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public unsafe T Deserialize<T>(byte[] bytes, int index, int count)
        {
            fixed (byte* ptr = &bytes[index])
            {
                using var stream = new UnmanagedMemoryStream(ptr, count);
                var @object = ProtoBuf.Serializer.Deserialize<T>(stream);
                if (@object is ASerialize aSerialize)
                {
                    aSerialize.AfterDeserialization();
                }
                return @object;
            }
        }
        /// <summary>
        /// 使用ProtoBuf反序列化数据到实例
        /// </summary>
        /// <param name="type"></param>
        /// <param name="bytes"></param>
        /// <param name="index"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public unsafe object Deserialize(Type type, byte[] bytes, int index, int count)
        {
            fixed (byte* ptr = &bytes[index])
            {
                using var stream = new UnmanagedMemoryStream(ptr, count);
                var @object = ProtoBuf.Serializer.Deserialize(type, stream);
                if (@object is ASerialize aSerialize)
                {
                    aSerialize.AfterDeserialization();
                }
                return @object;
            }
        }
        /// <summary>
        /// 使用ProtoBuf序列化某一个实例到IBufferWriter中
        /// </summary>
        /// <param name="object"></param>
        /// <param name="buffer"></param>
        /// <typeparam name="T"></typeparam>
        public void Serialize<T>(T @object, IBufferWriter<byte> buffer)
        {
            if (@object is ASerialize aSerialize)
            {
                aSerialize.BeginInit();
            }

            RuntimeTypeModel.Default.Serialize((MemoryStream)buffer, @object);
        }
        /// <summary>
        /// 使用ProtoBuf序列化某一个实例到IBufferWriter中
        /// </summary>
        /// <param name="object"></param>
        /// <param name="buffer"></param>
        public void Serialize(object @object, IBufferWriter<byte> buffer)
        {
            if (@object is ASerialize aSerialize)
            {
                aSerialize.BeginInit();
            }

            RuntimeTypeModel.Default.Serialize((MemoryStream)buffer, @object);
        }
        /// <summary>
        /// 使用ProtoBuf序列化某一个实例到IBufferWriter中
        /// </summary>
        /// <param name="type"></param>
        /// <param name="object"></param>
        /// <param name="buffer"></param>
        public void Serialize(Type type, object @object, IBufferWriter<byte> buffer)
        {
            if (@object is ASerialize aSerialize)
            {
                aSerialize.BeginInit();
            }

            RuntimeTypeModel.Default.Serialize((MemoryStream)buffer, @object);
        }
        private byte[] Serialize<T>(T @object)
        {
            if (@object is ASerialize aSerialize)
            {
                aSerialize.BeginInit();
            }
            
            var buffer = new MemoryStream();
            RuntimeTypeModel.Default.Serialize(buffer, @object);
            return buffer.ToArray();
        }
        /// <summary>
        /// 克隆
        /// </summary>
        /// <param name="t"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T Clone<T>(T t)
        {
            return Deserialize<T>(Serialize(t));
        }
    }
}
#endif
ProtoBufPackHelperUnity 类设计:Unity/Console 平台的 Protobuf 序列化实现

核心定位 :作为 ISerialize 接口的具体实现类,专为 Unity 和 Console 平台提供 Protobuf 协议的序列化与反序列化能力,通过适配平台特性的内存操作(如非托管内存流)实现 "业务对象↔字节流" 转换,是框架在客户端 / 控制台场景下 Protobuf 协议的核心载体。

核心标识与接口契约

  • 序列化器标识 :通过 SerializeName 属性固定标识为 ProtoBuf,作为框架动态匹配序列化器的依据(如解析器根据消息协议字段选择对应实现类),确保多协议场景下的路由一致性。
  • 接口实现 :严格遵循 ISerialize 接口契约,实现全部 6 个 Deserialize 重载、3 个 Serialize 重载及 Clone 方法,覆盖泛型 / 非泛型、不同输入输出载体(byte[]MemoryStreamBuffer)的场景,适配 Unity 平台的网络通信与数据处理需求。

初始化配置 :与 Net 平台不同,该类未显式定义构造函数,也未包含类型注册(如 IProto 类型扫描)和运行时编译(CompileInPlace)逻辑。这一设计适配 Unity 平台的特性 ------ 通常通过静态代码生成或提前注册类型避免运行时反射开销,简化了初始化流程以减少客户端性能损耗。

反序列化逻辑:平台适配的内存操作 :提供 6 个 Deserialize 重载方法,结合 Unity 平台允许的非托管内存操作,实现高效字节流解析:

  • 非托管内存流适配 :反序列化核心依赖 UnmanagedMemoryStreamunsafe 代码块:通过 fixed 关键字固定 byte[] 的内存地址(byte* ptr),直接创建基于非托管指针的流(new UnmanagedMemoryStream(ptr, length)),避免托管内存拷贝开销。这种方式适配 Unity 平台对原生内存操作的支持,提升解析效率。
  • 片段解析支持 :针对 byte[] 片段(indexcount 参数),通过 fixed (byte* ptr = &bytes[index]) 定位片段起始指针,创建长度为 countUnmanagedMemoryStream,直接解析缓冲区片段(如粘包中的消息体部分),无需提前提取完整数组。
  • 载体兼容 :同时支持 MemoryStreamBuffer(框架内存池化缓冲)解析,直接调用 ProtoBuf.Serializer.Deserialize 从流中读取数据,适配框架统一的缓冲管理机制。
  • 后处理机制 :若反序列化结果为 ASerialize 类型(框架序列化基类),自动调用 AfterDeserialization() 方法,执行状态恢复(如时间戳转 DateTime)、业务校验等反序列化后逻辑,与 Net 平台保持一致。

序列化逻辑:平台适配的缓冲写入 :实现 3 个 Serialize 重载方法,适配 Unity 平台的 Protobuf 接口特性:

  • 缓冲类型转换 :将 IBufferWriter<byte> 强制转换为 MemoryStream 进行写入((MemoryStream)buffer),这一设计适配 Unity 平台 Protobuf 库对 MemoryStream 的原生支持,避免跨接口转换的额外开销。
  • 预处理机制 :若序列化对象为 ASerialize 类型,先调用 BeginInit() 方法执行预处理:冻结对象状态(防止并发修改)、转换非序列化友好类型(如 DateTimeOffset 转时间戳),确保序列化数据的准确性。
  • 类型支持 :覆盖泛型(Serialize<T>)、任意对象(Serialize(object))、指定类型(Serialize(Type type, object))场景,适配客户端动态消息处理需求(如 UI 数据序列化、控制台指令封装)。

克隆功能Clone<T> 方法通过 "序列化→反序列化" 链路实现对象深拷贝:先调用私有 Serialize<T> 方法将原对象序列化为 byte[],再通过 Deserialize<T> 方法从字节数组还原为新对象,确保克隆体与原对象完全独立(避免引用类型共享状态),适配客户端场景中对象复用(如消息副本、数据快照)的需求。

Bson 序列化器:结构化数据的灵活实现

框架针对不同运行环境也提供了 Bson 序列化实现,均遵循 ISerialize 接口规范,适合处理复杂嵌套结构、动态字段的网络传输场景。其核心由 转换引擎(BsonPackHelper) 构成,同时预留了 "类型适配(StructBsonSerialize)" 和 "流程校验(SupportInitializeChecker)" 两个扩展点,形成 "核心功能 + 预留扩展" 的 BSON 序列化体系。鉴于两个扩展点框架未有引用,在此不做解析。

Net 平台:BsonPackHelperNet 实现

作为 ISerialize 接口实现,专为 .NET 环境提供 Bson 序列化能力,通过 MongoDB.Bson 库封装,支持复杂对象(如实体、嵌套结构)的 "对象↔字节流" 转换,是服务器端处理复杂业务数据的核心载体。其源码位于:Runtime/Core/Serialize/BsonPack/BsonPackHelperNet.cs

less 复制代码
#if FANTASY_NET
using System.Buffers;
using System.Collections;
using System.ComponentModel;
using System.Reflection;
using Fantasy.Assembly;
using Fantasy.Entitas;
using MongoDB.Bson;
using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Conventions;
using MongoDB.Bson.Serialization.Serializers;
#pragma warning disable CS8603 // Possible null reference return.
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
#pragma warning disable CS8602 // Dereference of a possibly null reference.

namespace Fantasy.Serialize
{
    /// <summary>
    /// BSON帮助方法
    /// </summary>
    public class BsonPackHelper : ISerialize
    {
        /// <summary>
        /// 序列化器的名字
        /// </summary>
        public string SerializeName { get; } = "Bson";
        
        /// <summary>
        /// 构造函数
        /// </summary>
        public BsonPackHelper()
        {
            // 清除掉注册过的LookupClassMap。

            var classMapRegistryField = typeof(BsonClassMap).GetField("__classMaps", BindingFlags.Static | BindingFlags.NonPublic);

            if (classMapRegistryField != null)
            {
                ((Dictionary<Type, BsonClassMap>)classMapRegistryField.GetValue(null)).Clear();
            }

            // 清除掉注册过的ConventionRegistry。

            var registryField = typeof(ConventionRegistry).GetField("_lookup", BindingFlags.Static | BindingFlags.NonPublic);

            if (registryField != null)
            {
                var registry = registryField.GetValue(null);
                var dictionaryField = registry.GetType().GetField("_conventions", BindingFlags.Instance | BindingFlags.NonPublic);
                if (dictionaryField != null)
                {
                    ((IDictionary)dictionaryField.GetValue(registry)).Clear();
                }
            }

            // 初始化ConventionRegistry、注册IgnoreExtraElements。

            ConventionRegistry.Register("IgnoreExtraElements", new ConventionPack { new IgnoreExtraElementsConvention(true) }, type => true);

            // 注册一个自定义的序列化器。

            // BsonSerializer.TryRegisterSerializer(typeof(float2), new StructBsonSerialize<float2>());
            // BsonSerializer.TryRegisterSerializer(typeof(float3), new StructBsonSerialize<float3>());
            // BsonSerializer.TryRegisterSerializer(typeof(float4), new StructBsonSerialize<float4>());
            // BsonSerializer.TryRegisterSerializer(typeof(quaternion), new StructBsonSerialize<quaternion>());
            BsonSerializer.RegisterSerializer(new ObjectSerializer(x => true));

            // 注册LookupClassMap。

            foreach (var type in AssemblySystem.ForEach())
            {
                if (type.IsInterface || type.IsAbstract || type.IsGenericType || !typeof(Entity).IsAssignableFrom(type))
                {
                    continue;
                }
                    
                BsonClassMap.LookupClassMap(type);
            }
        }

        /// <summary>
        /// 反序列化
        /// </summary>
        /// <param name="bytes"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T Deserialize<T>(byte[] bytes)
        {
            var @object = BsonSerializer.Deserialize<T>(bytes);
            if (@object is ASerialize aSerialize)
            {
                aSerialize.AfterDeserialization();
            }
            return @object;
        }

        /// <summary>
        /// 反序列化
        /// </summary>
        /// <param name="buffer"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T Deserialize<T>(MemoryStreamBuffer buffer)
        {
            var @object = BsonSerializer.Deserialize<T>(buffer);
            if (@object is ASerialize aSerialize)
            {
                aSerialize.AfterDeserialization();
            }
            return @object;
        }
    
        /// <summary>
        /// 反序列化
        /// </summary>
        /// <param name="type"></param>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public object Deserialize(Type type, byte[] bytes)
        {
            var @object = BsonSerializer.Deserialize(bytes, type);
            if (@object is ASerialize aSerialize)
            {
                aSerialize.AfterDeserialization();
            }
            return @object;
        }
    
        /// <summary>
        /// 反序列化
        /// </summary>
        /// <param name="type"></param>
        /// <param name="buffer"></param>
        /// <returns></returns>
        public object Deserialize(Type type, MemoryStreamBuffer buffer)
        {
            var @object = BsonSerializer.Deserialize(buffer, type);
            if (@object is ASerialize aSerialize)
            {
                aSerialize.AfterDeserialization();
            }
            return @object;
        }

        /// <summary>
        /// 反序列化
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="index"></param>
        /// <param name="count"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public unsafe T Deserialize<T>(byte[] bytes, int index, int count)
        {
            T @object;
            
            fixed (byte* ptr = &bytes[index])
            {
                using var stream = new UnmanagedMemoryStream(ptr, count);
                @object = BsonSerializer.Deserialize<T>(stream);
            }

            if (@object is ASerialize aSerialize)
            {
                aSerialize.AfterDeserialization();
            }

            return @object;
        }

        /// <summary>
        /// 反序列化
        /// </summary>
        /// <param name="type"></param>
        /// <param name="bytes"></param>
        /// <param name="index"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public unsafe object Deserialize(Type type, byte[] bytes, int index, int count)
        {
            object @object;
            
            fixed (byte* ptr = &bytes[index])
            {
                using var stream = new UnmanagedMemoryStream(ptr, count);
                @object = BsonSerializer.Deserialize(stream, type);
            }
            
            if (@object is ASerialize aSerialize)
            {
                aSerialize.AfterDeserialization();
            }

            return @object;
        }

        /// <summary>
        /// 序列化
        /// </summary>
        /// <param name="object"></param>
        /// <param name="buffer"></param>
        /// <typeparam name="T"></typeparam>
        public void Serialize<T>(T @object, IBufferWriter<byte> buffer)
        {
            if (@object is ASerialize aSerialize)
            {
                aSerialize.BeginInit();
            }

            using IBsonWriter bsonWriter =
                new BsonBinaryWriter((MemoryStream)buffer, BsonBinaryWriterSettings.Defaults);
            BsonSerializer.Serialize(bsonWriter, @object);
        }

        /// <summary>
        /// 序列化
        /// </summary>
        /// <param name="object"></param>
        /// <param name="buffer"></param>
        public void Serialize(object @object, IBufferWriter<byte> buffer)
        {
            if (@object is ASerialize aSerialize)
            {
                aSerialize.BeginInit();
            }
            
            using IBsonWriter bsonWriter =
                new BsonBinaryWriter((MemoryStream)buffer, BsonBinaryWriterSettings.Defaults);
            BsonSerializer.Serialize(bsonWriter, @object.GetType(), @object);
        }

        /// <summary>
        /// 序列化
        /// </summary>
        /// <param name="type"></param>
        /// <param name="object"></param>
        /// <param name="buffer"></param>
        public void Serialize(Type type, object @object, IBufferWriter<byte> buffer)
        {
            if (@object is ASerialize aSerialize)
            {
                aSerialize.BeginInit();
            }

            using IBsonWriter bsonWriter =
                new BsonBinaryWriter((MemoryStream)buffer, BsonBinaryWriterSettings.Defaults);
            BsonSerializer.Serialize(bsonWriter, type, @object);
        }

        /// <summary>
        /// 序列化并返回的长度
        /// </summary>
        /// <param name="type"></param>
        /// <param name="object"></param>
        /// <param name="buffer"></param>
        /// <returns></returns>
        public int SerializeAndReturnLength(Type type, object @object, MemoryStreamBuffer buffer)
        {
            if (@object is ASerialize aSerialize)
            {
                aSerialize.BeginInit();
            }
            
            using IBsonWriter bsonWriter = new BsonBinaryWriter(buffer, BsonBinaryWriterSettings.Defaults);
            BsonSerializer.Serialize(bsonWriter, type, @object);
            return (int)buffer.Length;
        }

        /// <summary>
        /// 序列化
        /// </summary>
        /// <param name="object"></param>
        /// <returns></returns>
        public static byte[] Serialize(object @object)
        {
            if (@object is ASerialize aSerialize)
            {
                aSerialize.BeginInit();
            }
            return @object.ToBson(@object.GetType());
        }
    
        /// <summary>
        /// 序列化
        /// </summary>
        /// <param name="object"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static byte[] Serialize<T>(T @object)
        {
            if (@object is ASerialize aSerialize)
            {
                aSerialize.BeginInit();
            }
            return @object.ToBson<T>();
        }
    
        /// <summary>
        /// 克隆
        /// </summary>
        /// <param name="t"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T Clone<T>(T t)
        {
            return Deserialize<T>(Serialize(t));
        }
    }
}
#endif
BsonPackHelper 类设计:Net 平台的 Bson 序列化实现

核心定位 :作为 ISerialize 接口的具体实现,专为 Net 平台提供 Bson 格式的序列化 / 反序列化能力,通过封装 MongoDB.Bson 库的核心组件,实现 "业务对象↔Bson 字节流" 的转换,是框架处理实体存储、特定网络通信的核心工具。

核心标识与接口契约

  • 序列化器标识SerializeName 固定为 "Bson",作为框架多协议场景中匹配 Bson 序列化器的 "唯一标识",确保解析器能准确选择它处理 Bson 格式数据。
  • 接口实现 :严格遵循 ISerialize 契约,实现 6 个 Deserialize 重载(支持 byte[]MemoryStreamBuffer、缓冲区片段等输入)、3 个 Serialize 重载(适配泛型 / 非泛型对象)及 Clone 方法,兼容框架统一的序列化流程。

重置并搭建 Bson 序列化环境:构造函数的核心是 "清理历史配置→注册规则→识别业务类型",为 Bson 序列化提供纯净环境:

  • 清理 __classMaps:重置类型映射缓存__classMapsBsonClassMap 类的静态私有字典字段Dictionary<Type, BsonClassMap>),缓存 "类型→序列化规则" 的映射。通过反射清空,避免旧规则冲突。
  • 清理 _lookup_conventions -- 重置序列化约定_lookupConventionRegistry静态私有容器字段 ,管理序列化规则集合; _conventions_lookup 内部的字典 ,存储 "约定名称→规则" 映射。反射清空 _conventions,确保旧约定不干扰新规则。
  • 注册核心约定:忽略多余字段ConventionPack :MongoDB.Bson 中存储一组序列化规则的容器 ,便于批量注册。这里创建包含 IgnoreExtraElementsConvention(true) 的约定包,规则为 "反序列化时忽略类中未定义的字段"。第三个参数 type => true 表示对所有类型生效,适配数据版本迭代场景。

注册自定义序列化器 :注册 ObjectSerializer(x => true),允许 Bson 处理动态类型,突破默认类型限制。

注册 Entity 派生类 -- 让 Bson 认识业务实体 :筛选 "非接口、非抽象、非泛型且继承自 Entity" 的类,调用 BsonClassMap.LookupClassMap(type) 注册映射,生成序列化规则并存入 __classMaps

反序列化逻辑:多场景解析 Bson 字节流 :提供 6 个 Deserialize 重载,适配不同数据来源:

  • UnmanagedMemoryStream :.NET 中用于直接操作非托管内存 的流类型,无需将数据复制到托管内存(如 byte[]),可直接通过内存指针访问数据,大幅减少内存拷贝开销。
  • 片段解析场景的应用 :在处理 byte[] 片段(indexcount 参数指定范围)时,代码通过 unsafe 代码块固定片段起始指针(fixed (byte* ptr = &bytes[index])),创建 UnmanagedMemoryStream(ptr, count)------ 直接访问从 index 开始、长度为 count 的内存区域,避免将片段复制到新 byte[] 中,尤其在处理大缓冲区或高频通信时,能显著提升性能(如解析粘包中的消息体片段)。
  • 其他载体支持 :从完整 byte[]MemoryStreamBuffer 解析时,直接调用 BsonSerializer.Deserialize,适配基础场景。
  • 后处理机制 :若结果是 ASerialize 类型,自动调用 AfterDeserialization() 完成状态恢复(如时间戳转 DateTime、校验必填字段)。

序列化逻辑:将对象转为 Bson 字节流 实现 3 个 Serialize 重载,核心是高效写入 Bson 数据:

  • IBufferWriter<byte> 转为 MemoryStream,通过 BsonBinaryWriter 写入数据,直接对接框架内存池缓冲。
  • 若对象是 ASerialize 类型,先调用 BeginInit() 冻结状态、清理临时数据,确保数据准确。
  • 扩展方法 SerializeAndReturnLength 返回序列化长度(用于数据包头部计算),静态 Serialize 方法直接返回 byte[](适配临时克隆场景)。

克隆功能:通过 "序列化→反序列化" 复制对象,确保新对象与原对象完全独立,适配实体快照、消息副本等场景。

Unity 平台:BsonPackHelper 实现

针对 Unity 平台优化的 Bson 序列化实现,遵循 ISerialize 接口,通过非托管内存操作适配平台特性,兼顾复杂结构处理与客户端性能需求。其源码位于:Runtime/Core/Serialize/BsonPack/BsonPackHelperUnity.cs

BsonPackHelper 类设计:Unity 平台的 Bson 序列化实现

Unity 平台下 ISerialize 接口的具体实现,作为 Bson 序列化功能的预留载体,暂未实现实际序列化逻辑,无 Net 平台的初始化配置(如类型注册、规则约定),主要作用是保障 Unity 环境下框架接口结构完整,为后续实现 Bson 功能预留扩展点,避免因功能缺失导致框架依赖中断(仅抛 NotImplementedException)。

序列化管理器:全局调度与协议适配的核心

SerializerManager 负责序列化器的注册、获取与全局管理,是连接消息类型与序列化协议的核心枢纽。其源码位于 Runtime/Core/Serialize/SerializerManager.cs

csharp 复制代码
using System;
using System.Collections.Generic;
using Fantasy.Assembly;
using Fantasy.Helper;
#if !FANTASY_EXPORTER
using Fantasy.Network;
#endif
using ProtoBuf;
#pragma warning disable CS8604 // Possible null reference argument.
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
#pragma warning disable CS8602 // Dereference of a possibly null reference.
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.

namespace Fantasy.Serialize
{
    /// <summary>
    /// 框架内置的序列化器类型
    /// </summary>
    public static class FantasySerializerType
    {
        /// <summary>
        /// ProtoBuf在SerializerManager的数组下标
        /// </summary>
        public const int ProtoBuf = 0;
        /// <summary>
        /// Bson在SerializerManager的数组下标
        /// </summary>
        public const int Bson = 1;
    }
    
    /// <summary>
    /// 管理序列化静态方法,主要是优化网络协议时使用。
    /// </summary>
    public static class SerializerManager
    {
        private static ISerialize[] _serializers;
        private static bool _isInitialized = false;

#if FANTASY_NET || FANTASY_UNITY
        /// <summary>
        /// 初始化方法
        /// </summary>
        public static void Initialize()
        {
            if (_isInitialized)
            {
                return;
            }

            try
            {
                var sort = new SortedList<long, ISerialize>();
            
                foreach (var serializerType in AssemblySystem.ForEach(typeof(ISerialize)))
                {
                    var serializer = (ISerialize)Activator.CreateInstance(serializerType);
                    var computeHash64 = HashCodeHelper.ComputeHash64(serializer.SerializeName);
                    sort.Add(computeHash64, serializer);
                }

#if FANTASY_NET
                var index = 1;
#endif
#if FANTASY_UNITY
                var index = 0;
#endif
                
                _serializers = new ISerialize[sort.Count];
            
                foreach (var (_, serialize) in sort)
                {
                    var serializerIndex = 0;
                    
                    switch (serialize)
                    {
                        case ProtoBufPackHelper:
                        {
                            serializerIndex = FantasySerializerType.ProtoBuf;
                            break;
                        }
#if FANTASY_NET
                        case BsonPackHelper:
                        {
                            serializerIndex = FantasySerializerType.Bson;
                            break;
                        }    
#endif
                        default:
                        {
                            serializerIndex = ++index;
                            break;
                        }
                    }
                
                    _serializers[serializerIndex] = serialize;
                }
            
                _isInitialized = true;
            }
            catch
            {
                Dispose();
                throw;
            }
        }
#else
        /// <summary>
        /// 初始化方法
        /// </summary>
        public static void Initialize()
        {
            if (_isInitialized)
            {
                return;
            }
            
            _serializers = new ISerialize[1];
            _serializers[0] = new ProtoBufPackHelper();
        }
#endif

        /// <summary>
        /// 销毁方法
        /// </summary>
        public static void Dispose()
        {
            _isInitialized = false;
            Array.Clear(_serializers, 0, _serializers.Length);
        }

        /// <summary>
        /// 根据协议类型获取序列化器
        /// </summary>
        /// <param name="opCodeProtocolType"></param>
        /// <returns></returns>
        public static ISerialize GetSerializer(uint opCodeProtocolType)
        {
            return _serializers[opCodeProtocolType];
        }
        
        /// <summary>
        /// 获得一个序列化器
        /// </summary>
        /// <param name="opCodeProtocolType"></param>
        /// <param name="serializer"></param>
        /// <returns></returns>
        public static bool TryGetSerializer(uint opCodeProtocolType, out ISerialize serializer)
        {
            if (opCodeProtocolType < _serializers.Length)
            {
                serializer = _serializers[opCodeProtocolType];
                return true;
            }

            serializer = default;
            return false;
        }
    }
}

SerializerManager 类设计:框架序列化器的全局管控中枢

核心定位 :作为框架序列化体系的 "调度核心",负责所有ISerialize接口实现类的统一管理,通过整合 "索引定义 - 实例初始化 - 高效查询" 功能,实现 "协议类型标识→序列化器" 的精准映射,支撑多协议场景下 "内存对象↔传输字节流" 的转换需求,是分布式通信的基础调度组件。

核心标识与接口契约

框架通过FantasySerializerType静态类为内置序列化器定义固定索引 ------ProtoBuf=0Bson=1,形成 "类型 - 位置" 的基准映射,确保任何环境下通过索引都能稳定定位对应序列化器,避免动态分配导致的协议解析混乱。所有被管理的序列化器必须实现ISerialize接口,该接口统一了Serialize(序列化)、Deserialize(反序列化)等核心方法签名,使管理器无需关注具体实现,仅通过接口即可调用任意序列化器功能,屏蔽差异的同时保障多实现兼容。

多环境适配的初始化机制

初始化逻辑围绕 "扫描发现→有序排序→索引分配→实例存储" 展开,根据运行环境(通过FANTASY_NET/FANTASY_UNITY条件编译区分)提供差异化实现。全功能环境下,先通过AssemblySystem.ForEach(typeof(ISerialize))遍历程序集,自动发现所有ISerialize实现类(包括内置的ProtoBufPackHelperBsonPackHelper和自定义序列化器);再以序列化器SerializeName的哈希值为键存入SortedList,确保不同环境、不同运行次数下加载顺序一致;随后按规则分配索引 ------ 内置序列化器绑定FantasySerializerType定义的固定索引(0、1),自定义序列化器从index=2开始递增分配,避免冲突且支持无限扩展;若初始化失败,通过Log.Error记录异常并调用Dispose清理资源,防止框架崩溃。简化环境下则仅初始化ProtoBufPackHelper并存入索引 0,精简资源占用以满足轻量场景需求。

生命周期管理:资源的安全回收
Dispose方法负责序列化器集合的清理,具体通过重置_isInitializedfalse,并调用Array.Clear清空存储序列化器实例的_serializers数组,释放所有实例引用以避免内存泄漏,适配程序重启或环境切换场景。

序列化器查询接口:高效精准的映射

提供两种查询方式平衡性能与安全性:GetSerializer方法以协议类型标识(opCodeProtocolType)为索引,直接访问_serializers数组获取序列化器,查询复杂度为 O (1),适配高频网络通信;TryGetSerializer方法先校验索引有效性(opCodeProtocolType < _serializers.Length),再返回序列化器并通过bool结果标识查询成功与否,避免越界异常,为上层逻辑提供安全处理入口。

总结:Fantasy 框架序列化机制的架构价值与场景适配

Fantasy 框架的消息设计与序列化机制,通过 "消息抽象 - 序列化实现 - 全局调度" 的深度协同,构建了分布式通信的完整解决方案,核心价值与架构优势体现为:

  • 消息抽象双重架构:以 IMessage 接口定契约(OpCode 标识、功能分类),AMessage 抽象类供基础能力(序列化生命周期、对象池化等),既保证消息处理一致性,又通过接口继承适配请求响应、跨节点路由等场景,奠定通信基石。
  • 序列化多维度适配:以 ISerialize 为契约,实现 Protobuf(高效紧凑)与 Bson(适配复杂结构)序列化器,针对.NET 与 Unity 平台优化底层,结合 ASerialize 生命周期钩子衔接业务逻辑,兼顾性能与灵活性。
  • 全局调度协同运转:MessageDispatcherComponent 解决消息精准路由,SerializerManager 实现序列化器动态匹配,二者协同完成 "业务对象→字节流→目标对象" 全链路转换,支撑高频多样的跨节点通信。

整体架构既保证分布式通信高效性(如紧凑传输、GC 优化),又通过接口抽象支持扩展灵活性(如新增协议、自定义消息),形成适配复杂场景的 "消息通信神经中枢"。

相关推荐
LKAI.32 分钟前
传统方式部署(RuoYi-Cloud)微服务
java·linux·前端·后端·微服务·node.js·ruoyi
Victor35643 分钟前
Redis(11)如何通过命令行操作Redis?
后端
Victor35644 分钟前
Redis(10)如何连接到Redis服务器?
后端
快手技术3 小时前
快手Klear-Reasoner登顶8B模型榜首,GPPO算法双效强化稳定性与探索能力!
后端
二闹3 小时前
三个注解,到底该用哪一个?别再傻傻分不清了!
后端
用户49055816081253 小时前
当控制面更新一条 ACL 规则时,如何更新给数据面
后端
林太白3 小时前
Nuxt.js搭建一个官网如何简单
前端·javascript·后端
码事漫谈3 小时前
VS Code 终端完全指南
后端
该用户已不存在3 小时前
OpenJDK、Temurin、GraalVM...到底该装哪个?
java·后端