主题
强类型消息系统 (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<T>
| 属性/方法 | 说明 |
|---|---|
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<T> | ProtoCustomMessage |
|---|---|---|
| 类型安全 | 编译时检查 | 运行时检查 |
| 序列化 | 自动 JSON 序列化 | 手动处理 |
| 消息路由 | 基于类型自动分发 | 手动解析分发 |
| 性能开销 | 中等(JSON 序列化) | 低(直接字节) |
| 灵活性 | 受 JSON 限制 | 支持任意格式 |
序列化注意事项
- 不支持多维数组
[,](System.Text.Json 限制),用一维数组替代 - 不能序列化
Player等引擎对象,用 ID 替代 - 使用框架的
GameJsonSerializerOptions.Default选项 - 消息 payload 数据必须使用公开
get; set;属性;框架默认 JSON 配置未开启IncludeFields,普通字段不会参与序列化
相关文档
- 双端事件系统 (DualEndEvent) — 触发器模式跨端通信
- 消息传递系统 (ProtoCustomMessage) — 底层二进制通信
- 异步编程最佳实践