深入剖析 Fantasy 框架的消息设计与序列化机制
引言:消息与序列化的分布式通信基石
在分布式系统中,消息是组件间通信的 "语言",而序列化则是 "语言翻译器"------ 它将内存中的业务对象转换为可传输的字节流,同时将接收的字节流还原为可操作的对象。Fantasy
框架作为面向分布式场景的开发框架,其消息设计与序列化机制经过精心打磨,既保证了跨平台通信的兼容性,又兼顾了高性能与扩展性,成为连接分布式节点的 "神经传导系统"。
本文将从源码层面深度剖析Fantasy
框架的消息设计与序列化机制,围绕消息接口抽象、序列化器实现、类型映射管理、多协议适配等核心维度,拆解从业务对象到网络字节流的完整转换链路。
框架设计概览:消息与序列化的整体架构
Fantasy
框架的消息与序列化机制采用「消息抽象 - 序列化实现 - 全局调度」的三层架构,通过 "接口规范 + 抽象类实现" 的组合设计,实现了消息格式定义与序列化逻辑的解耦,同时兼顾跨场景通信的灵活性与高效性。
核心组件关系图谱
从代码结构来看,整个机制的核心组件可归纳为以下四类,涵盖消息与序列化的全流程,且明确体现了 "接口 + 抽象类" 的协同设计:
- 消息抽象层 :以
IMessage
为核心的接口体系定义消息的行为契约(如OpCode()
标识、请求 / 响应分类);以AMessage
为核心的抽象类体系提供消息的基础功能实现(序列化生命周期管理、对象池支持、场景上下文关联等) - 序列化层 :
ISerialize
抽象接口定义序列化的通用规范;ProtobufSerializer
、BsonSerializer
等具体实现适配不同协议,负责将消息对象与字节流互转。 - 管理层 :
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
等)为分布式系统提供路由能力,支持跨节点消息传递(如从网关服务器路由至业务服务器);IRequest
与IResponse
构建 "请求 - 响应" 模式,IResponse
的ErrorCode
字段统一标准化错误反馈(如 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()
,确保对象在反序列化完成后能自动执行后续初始化逻辑(如字段校验、状态恢复),为上层序列化框架(如 ProtoBuf
、JSON
)提供了统一的生命周期钩子。
资源自动释放支持 :实现 IDisposable
接口,定义 Dispose()
虚方法,为所有子类提供了资源释放的标准化入口。在需要手动管理非托管资源(如网络连接句柄、临时缓存)的场景中,子类可重写该方法实现资源回收,避免内存泄漏。
序列化框架适配基础 :作为所有可序列化对象的基类,ASerialize
奠定了与各类序列化框架(ProtoBuf
、JSON
、MongoDB.Bson
等)的适配基础。其通过虚方法设计(如 EndInit()
、Dispose()
)为子类预留了扩展点,使得派生类能够在不破坏基础规范的前提下,集成具体的序列化注解与逻辑。
AMessage:消息的基础功能实现与序列化支持
继承自 ASerialize
,与 IMessage
的 "规范定义" 不同,AMessage
专注于提供 "how to do" 的基础功能实现,是消息 "物理特性" 的载体,在 ASerialize
的基础上进一步封装了消息特有的核心能力。
AMessage 的核心支撑:从序列化适配到场景协同
序列化能力集成 :基于 ASerialize
的底层支撑,内置与主流序列化框架(如 ProtoBuf
、JSON
)的适配逻辑。所有继承 AMessage
的消息类无需额外编码即可支持序列化 / 反序列化操作,极大简化了消息在网络传输、存储中的处理流程。
对象池化管理 :实现 IPool
接口,提供 IsPool()
、SetIsPool()
等方法,支持消息对象的池化复用。在高频消息交互场景(如游戏中的帧同步、实时通信)中,可减少对象频繁创建 / 销毁带来的内存开销,提升系统性能。
场景上下文绑定 :包含 SetScene()
、GetScene()
等方法,支持消息与特定场景(Scene
)的关联。在多场景架构(如游戏中的多地图、分布式系统中的多服务节点)中,可快速定位消息的上下文环境,简化业务逻辑中的场景依赖处理。
协同模式:组合构建完整消息类型
IMessage
与 AMessage
属于不同层面的抽象,二者无直接继承关系;在实际业务中,具体消息类通过 "继承 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]
,避免冗余数据传输。例如PingRequest
的RpcId
通过[ProtoMember(1)]
指定序列化顺序,既满足网络传输需求,又简化了序列化逻辑的实现。
请求 - 响应的标准化配对 :请求类消息(如BenchmarkRequest
、PingRequest
)通过ResponseType
字段与对应响应类(如BenchmarkResponse
、PingResponse
)绑定,配合RpcId
字段实现请求与响应的精准匹配。让框架能自动将响应关联到发起的请求,确保分布式环境下通信的可靠性 ------ 例如PingRequest
的RpcId
会在PingResponse
中带回,用于定位对应的请求回调。
分布式场景的精准适配:针对不同分布式通信需求,消息类通过字段设计与接口实现实现场景化适配:
- 基础通信场景:
PingRequest
/PingResponse
通过Now
字段传递时间戳,支持节点心跳检测; - 地址管理场景:
I_AddressableAdd_Request
等通过AddressableId
和RouteId
,实现分布式对象的跨节点定位与管理; - 漫游场景:
I_LinkRoamingRequest
/I_UnLinkRoamingRequest
通过RoamingId
、TerminusId
等字段,支持对象在节点间迁移时的消息跟随与链路维护。
消息注册与 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→PingRequest
、LoginRequest
")。卸载程序集时,通过该列表批量获取请求类型,从_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> _customRouteMap
与OneToManyList<long, long> _assemblyCustomRouteMap
:前者存储 "OpCode→路由类型" 的映射(如1003 → 2
),支撑跨节点消息的自定义路由策略;后者记录 "程序集标识→路由 OpCode" 的关联,用于卸载时批量清理旧路由规则。 - (服务端)
Dictionary<Type, IRouteMessageHandler> _routeMessageHandlers
与OneToManyList<long, HandlerInfo<IRouteMessageHandler>> _assemblyRouteMessageHandlers
:前者存储 "路由消息类型→路由处理器" 的映射(如typeof(CrossServerChat) → CrossServerChatHandler
),专门处理跨节点交互;后者记录程序集关联,用于卸载时批量清理路由处理器映射。
线程安全字段:保障分布式并发安全 :- CoroutineLock _receiveRouteMessageLock
:服务端路由消息的协程锁,通过 Scene.CoroutineLockComponent.Create
初始化,以组件类型哈希值为锁键。确保同一实体的路由消息(如玩家跨服操作)通过 _receiveRouteMessageLock.Wait(runtimeId)
串行执行,避免并发修改实体状态导致的数据不一致。
核心方法:映射关系的动态管理流程
Initialize
:组件启动入口 :初始化路由消息的协程锁(_receiveRouteMessageLock
),为服务端线程安全处理提供基础;将组件注册到 AssemblySystem
,使其能被框架感知并在程序集加载时自动触发 Load
方法,完成初始映射注册。 Load
与LoadInner
:映射关系的创建:
Load
:负责线程调度,将加载逻辑投递到组件所属Scene
的逻辑线程执行,避免多线程并发修改字段导致的错乱。LoadInner
:核心逻辑实现,通过扫描程序集中的IMessage
、IMessageHandler
等类型,反射创建实例并提取关键信息(如OpCode
、Type()
),填充_networkProtocols
、_messageHandlers
等核心字段,同时通过OneToManyList
记录程序集关联,为后续卸载做准备。OnUnLoad
与OnUnLoadInner
:映射关系的清理: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
框架通过接口抽象与多实现,支持不同序列化协议(如 ProtoBuf
、Bson
),并通过管理器实现灵活调度。
ISerialize:序列化逻辑的「宪法性文件」
ISerialize
是连接 "字节流" 与 "业务对象" 的核心接口,它定义了数据序列化(业务对象→字节流)与反序列化(字节流→业务对象)的标准契约。解析器的 Pack
(打包)和 UnPack
(解包)方法均通过该接口实现与具体序列化协议(如 Protobuf
、Bson
等)无关的数据转换,是框架支持多协议适配的关键组件。其源码位于 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
属性定义序列化器的唯一名称(如 Protobuf
、Bson
),作为协议中指定序列化方式的标识依据。框架可通过该名称动态匹配对应的序列化器,实现多协议场景下的灵活切换(例如根据消息头中的协议类型字段,选择对应 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
的设计核心是「通用契约 + 最小约束」,为 Protobuf
、Bson
等不同协议的实现预留了足够的扩展空间,同时保证上层逻辑能通过统一接口调用任意序列化器。
具体序列化器实现:从 ProtoBuf 到 Bson 的多协议适配
框架针对不同场景提供了两种核心序列化器:ProtoBufSerialize
(高效紧凑,适合网络传输)和 BsonSerialize
(支持复杂类型,适合数据存储与跨语言交互)。
Protobuf 序列化器:高性能二进制协议的实现
Protobuf
是 Google
推出的二进制序列化协议,具有体积小、解析快的特点,是分布式网络通信的理想选择。Fantasy
框架针对 .NET 与 Unity 环境分别提供了实现:ProtobufPackHelperNet
与 ProtobufPackHelperUnity
。均遵循 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 = true
和InferTagFromNameDefault = true
简化 Protobuf 标签定义(默认以字段名推断标签,零值字段隐式处理)。 - 类型注册 :遍历所有实现
IProto
接口的类型(通过AssemblySystem.ForEach(typeof(IProto))
扫描,其中IProto
作为空接口,其核心作用是标记接口 ),调用RuntimeTypeModel.Default.Add(type, true)
将类型注册到模型中,确保这些业务类型可被Protobuf
识别并正确序列化,为后续编解码提供类型支持。
反序列化逻辑 :提供 6 个 Deserialize
重载方法,覆盖多场景字节流解析需求,并结合 ASerialize
实现反序列化后处理:
- 输入载体适配 :支持从
byte[]
(封装为ReadOnlyMemory<byte>
实现零复制访问)、MemoryStreamBuffer
(直接复用流接口)解析,支持通过index
和count
参数截取缓冲区片段解析(适配粘包 / 分包场景)。 - 类型支持 :提供泛型(
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 平台允许的非托管内存操作,实现高效字节流解析:
- 非托管内存流适配 :反序列化核心依赖
UnmanagedMemoryStream
与unsafe
代码块:通过fixed
关键字固定byte[]
的内存地址(byte* ptr
),直接创建基于非托管指针的流(new UnmanagedMemoryStream(ptr, length)
),避免托管内存拷贝开销。这种方式适配 Unity 平台对原生内存操作的支持,提升解析效率。 - 片段解析支持 :针对
byte[]
片段(index
和count
参数),通过fixed (byte* ptr = &bytes[index])
定位片段起始指针,创建长度为count
的UnmanagedMemoryStream
,直接解析缓冲区片段(如粘包中的消息体部分),无需提前提取完整数组。 - 载体兼容 :同时支持
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
:重置类型映射缓存 :__classMaps
是BsonClassMap
类的静态私有字典字段 (Dictionary<Type, BsonClassMap>
),缓存 "类型→序列化规则" 的映射。通过反射清空,避免旧规则冲突。 - 清理
_lookup
与_conventions
-- 重置序列化约定 :_lookup
是ConventionRegistry
的静态私有容器字段 ,管理序列化规则集合;_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[]
片段(index
和count
参数指定范围)时,代码通过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=0
、Bson=1
,形成 "类型 - 位置" 的基准映射,确保任何环境下通过索引都能稳定定位对应序列化器,避免动态分配导致的协议解析混乱。所有被管理的序列化器必须实现ISerialize
接口,该接口统一了Serialize
(序列化)、Deserialize
(反序列化)等核心方法签名,使管理器无需关注具体实现,仅通过接口即可调用任意序列化器功能,屏蔽差异的同时保障多实现兼容。
多环境适配的初始化机制 :
初始化逻辑围绕 "扫描发现→有序排序→索引分配→实例存储" 展开,根据运行环境(通过FANTASY_NET
/FANTASY_UNITY
条件编译区分)提供差异化实现。全功能环境下,先通过AssemblySystem.ForEach(typeof(ISerialize))
遍历程序集,自动发现所有ISerialize
实现类(包括内置的ProtoBufPackHelper
、BsonPackHelper
和自定义序列化器);再以序列化器SerializeName
的哈希值为键存入SortedList
,确保不同环境、不同运行次数下加载顺序一致;随后按规则分配索引 ------ 内置序列化器绑定FantasySerializerType
定义的固定索引(0、1),自定义序列化器从index=2
开始递增分配,避免冲突且支持无限扩展;若初始化失败,通过Log.Error
记录异常并调用Dispose
清理资源,防止框架崩溃。简化环境下则仅初始化ProtoBufPackHelper
并存入索引 0,精简资源占用以满足轻量场景需求。
生命周期管理:资源的安全回收
Dispose
方法负责序列化器集合的清理,具体通过重置_isInitialized
为false
,并调用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 优化),又通过接口抽象支持扩展灵活性(如新增协议、自定义消息),形成适配复杂场景的 "消息通信神经中枢"。