深入剖析 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 优化),又通过接口抽象支持扩展灵活性(如新增协议、自定义消息),形成适配复杂场景的 "消息通信神经中枢"。

相关推荐
爱勇宝8 小时前
第 1 章:别把“需求文档”当成真正的需求
前端·后端·程序员
IT_陈寒13 小时前
闭包陷阱让我加了两天班,JavaScript你真行
前端·人工智能·后端
易协同低代码14 小时前
通达OA核心类库TD类深度解析
后端
Gopher_HBo14 小时前
Go语言学习笔记(十八)Gin处理Session
后端
谭光志15 小时前
工具塞满上下文窗口怎么办?深度拆解 AI Agent Tool Search 按需加载实现原理
前端·后端·ai编程
她说..15 小时前
Java 默认值设置方式
java·开发语言·后端·springboot
foggyprojects15 小时前
从0开始,一句话启动AI DataAgent
后端·数据分析·ai编程
郡杰15 小时前
一些基础和问题解决
后端
陈随易15 小时前
前端项目部署只要30秒
前端·后端·程序员
YIAN15 小时前
从零手写文件读取 MCP 服务:一文吃透 Model Context Protocol 全链路通信原理
前端·后端·mcp