Skip to content

强类型消息系统 (TypedMessage System) ​

TypedMessage<T> 在 ProtoCustomMessage 基础上提供类型安全、自动 JSON 序列化和基于类型的自动路由。

> 定位说明:TypedMessage 是框架三层跨端通信机制的中间层。 > - 更简单? → 使用 双端事件 (DualEndEvent) > - 更高性能? → 使用 ProtoCustomMessage

系统架构 ​

应用层: MessageBuilder<T> + TypedMessage<T>
处理层: TypedMessageHandler + MessageHandlerAttribute
传输层: ProtoCustomMessage + TypedMessageEnvelope
网络层: ProtocolClientTransient + ProtocolServerTransient

定义消息类型 ​

消息类型是普通的 C# 类,通过 System.Text.Json 序列化:

csharp
public class PlayerMoveMessage
{
    public int PlayerId { get; set; }
    public float X { get; set; }
    public float Y { get; set; }
    public float Z { get; set; }
    public DateTime Timestamp { get; set; } = DateTime.UtcNow;
}

public class GameStateUpdateMessage
{
    public int CurrentWave { get; set; }
    public int PlayersAlive { get; set; }
    public TimeSpan TimeRemaining { get; set; }
    public List<PlayerStatus> PlayerStates { get; set; } = new();
}

发送消息 ​

客户端发送到服务器 ​

csharp
#if CLIENT
var message = new TypedMessage<PlayerMoveMessage>(new PlayerMoveMessage
{
    PlayerId = Player.LocalPlayer.Id,
    X = 10.5f, Y = 0.0f, Z = 20.3f
});

bool success = message.SendToServer();

// 异步发送
bool asyncSuccess = await message.SendToServerAsync();
#endif

服务器发送到客户端 ​

csharp
#if SERVER
var message = new TypedMessage<GameStateUpdateMessage>(stateMessage);

// 广播给所有玩家
message.Broadcast();

// 发送给特定玩家
message.SendTo(targetPlayer);

// 条件广播
message.Broadcast(player => player.IsOnline);

// 异步发送
await message.SendToAsync(targetPlayer);
await message.BroadcastAsync();
#endif

接收消息 ​

手动注册处理器 ​

csharp
public class MessageHandlers : IGameClass
{
    public static void OnRegisterGameClass()
    {
        Game.OnGameTriggerInitialization += () =>
        {
            TypedMessageHandler.Initialize();
            RegisterHandlers();
        };
    }

    private static void RegisterHandlers()
    {
#if CLIENT
        TypedMessageHandler.Register<GameStateUpdateMessage>(OnGameStateUpdate,
            MessagePriority.High, "GameStateHandler");
#endif

#if SERVER
        TypedMessageHandler.Register<PlayerMoveMessage>(OnPlayerMove,
            MessagePriority.High, "PlayerMoveHandler");
#endif
    }

#if CLIENT
    private static async Task<bool> OnGameStateUpdate(Player? sender, GameStateUpdateMessage message)
    {
        Game.Logger.LogInformation("游戏状态更新: Wave {Wave}", message.CurrentWave);
        return true;
    }
#endif

#if SERVER
    private static async Task<bool> OnPlayerMove(Player? sender, PlayerMoveMessage message)
    {
        if (sender == null) return false;
        Game.Logger.LogInformation("玩家 {PlayerId} 移动", sender.Id);
        return true;
    }
#endif
}

基于特性的自动注册 ​

使用 [MessageHandler] 特性标记的方法会被自动注册:

csharp
public class AutoHandlers
{
    [MessageHandler(Priority = MessagePriority.High, Name = "AutoGameStateHandler")]
    public static async Task<bool> HandleGameState(Player? sender, GameStateUpdateMessage message)
    {
        return true;
    }

    [MessageHandler(Priority = MessagePriority.Normal, IsAsync = false)]
    public static bool HandleChat(Player? sender, ChatMessage message)
    {
        return true;
    }
}

处理器方法签名 ​

csharp
// 正确的签名格式
[MessageHandler]
public static async Task<bool> Handler1(Player? sender, MyMessage message) { return true; }

[MessageHandler]
public static Task<bool> Handler2(Player? sender, MyMessage message) { return Task.FromResult(true); }

[MessageHandler]
public static bool Handler3(Player? sender, MyMessage message) { return true; }

要求:

  • 必须是 public static
  • 返回类型为 Task<bool> 或 bool
  • 参数为 (Player? sender, T message)

MessageBuilder 流畅 API ​

MessageBuilder<T> 提供链式 API:

csharp
#if CLIENT
// 客户端发送
await MessageBuilder<PlayerMoveMessage>
    .Create(new PlayerMoveMessage { X = 10, Y = 20, Z = 30 })
    .WithPriority(MessagePriority.High)
    .WithTimeout(TimeSpan.FromSeconds(5))
    .SendToServerAsync();
#endif

#if SERVER
// 服务器端:带过滤条件的广播
MessageBuilder<EventMessage>
    .Create(eventData)
    .WithPriority(MessagePriority.High)
    .ToPlayersWhere(player => player.IsOnline)
    .Send();

// 发送给特定玩家
MessageBuilder<NotificationMessage>
    .Create(notificationData)
    .ToPlayer(targetPlayer)
    .Send();

// 发送给多个玩家
MessageBuilder<TeamMessage>
    .Create(teamData)
    .ToPlayers(player1, player2, player3)
    .Send();

// 异步发送
await MessageBuilder<ImportantMessage>
    .Create(data)
    .WithPriority(MessagePriority.Critical)
    .SendAsync();
#endif

可靠消息发送 ​

框架提供 ReliableMessageSender,支持确认和重试:

csharp
#if SERVER
var sender = new ReliableMessageSender();

bool success = await sender.SendReliableAsync(
    new ImportantMessage { Content = "关键数据" },
    targetPlayer,
    timeout: TimeSpan.FromSeconds(10)
);

// 处理确认回调
sender.OnMessageAcknowledged(messageId);

// 使用完毕后释放
sender.Dispose();
#endif

消息队列管理 ​

MessageQueueManager 实现 IThinker,按优先级批量处理消息:

csharp
var queueManager = new MessageQueueManager();

// 将消息加入队列
queueManager.EnqueueMessage(typedMessage);

// 队列每帧自动按优先级处理(Critical > High > Normal > Low)
// 每帧最多处理 10 条消息

消息统计 ​

csharp
// 获取消息收发统计
IReadOnlyDictionary<string, MessageStats> stats = TypedMessageMetrics.GetStats();

foreach (var (messageType, stat) in stats)
{
    Game.Logger.LogInformation("{Type}: 发送={Sent}, 接收={Received}, 总字节={Bytes}",
        messageType, stat.SentCount, stat.ReceivedCount, stat.TotalBytes);
}

// 重置统计
TypedMessageMetrics.Reset();

核心类型参考 ​

TypedMessage&lt;T&gt; ​

属性/方法说明
Data消息数据(T 类型)
Priority消息优先级(MessagePriority 枚举)
Timeout超时时间(可选)
Timestamp时间戳
MessageId消息 ID(Guid)
Serialize()序列化为字节数组
Deserialize(byte[])静态方法,反序列化
SendToServer()客户端发送到服务器
SendToServerAsync()异步发送到服务器
SendTo(Player)服务器发送给玩家
SendToAsync(Player)异步发送给玩家
Broadcast(filter?)服务器广播
BroadcastAsync(filter?)异步广播

MessagePriority ​

值说明
Low低优先级(统计、日志)
Normal普通优先级(聊天、通知)
High高优先级(游戏状态、玩家操作)
Critical最高优先级(紧急事件)

TypedMessageHandler ​

方法说明
Initialize()初始化处理器系统
Register<T>(handler, priority?, name?)手动注册处理器
RemoveHandlers<T>()移除指定类型的所有处理器
ClearAllHandlers()清除所有处理器
GetHandlerStats()获取处理器统计

与 ProtoCustomMessage 对比 ​

特性TypedMessage&lt;T&gt;ProtoCustomMessage
类型安全编译时检查运行时检查
序列化自动 JSON 序列化手动处理
消息路由基于类型自动分发手动解析分发
性能开销中等(JSON 序列化)低(直接字节)
灵活性受 JSON 限制支持任意格式

序列化注意事项 ​

  • 不支持多维数组 [,](System.Text.Json 限制),用一维数组替代
  • 不能序列化 Player 等引擎对象,用 ID 替代
  • 使用框架的 GameJsonSerializerOptions.Default 选项
  • 消息 payload 数据必须使用公开 get; set; 属性;框架默认 JSON 配置未开启 IncludeFields,普通字段不会参与序列化

相关文档 ​