携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第40天,点击查看活动详情
Mirror网络消息机制详解
使用Mirror开发联网游戏时,我们一般使用Commands
,RPC
,SynVar
这些高层API来进行通讯和同步。而这些机制的底层是Network Message
。比如Commands
的背后是CommandMessage
。本文从消息的定义注册和发送层层深入,并分析消息的打包和批量发送机制。最后介绍自定义消息的用法。
网络消息接口
namespace Mirror
{
public interface NetworkMessage {}
}
NetworkMessage
接口,这是一个哑接口,没有任何的成员,只是为了标识实现该接口的结构体是一个网络消息。
Mirror预定义网络消息
在Messages.cs
中有Mirror预定义的网络消息。例如:
- AddPlayerMessage
- SceneMessage
- CommandMessage
- RpcMessage
- SpawnMessage
- ChangeOwnerMessage
- ObjectDestroyMessage
另外Mirror的Discovery机制和Authenticator机制也定义了几种网络消息,例如ServerRequest也是一个NetworkMessage。
NetworkMessage Handler
网络消息从服务器发往客户端或者反方向发送,因此无论服务器还是客户端都需要注册网络消息的Hanlder。
服务器注册消息Handler
使用:
NetworkServer.RegisterHandler<T>(Action<NetworkConnectionToClient, T> handler, bool requireAuthentication = true);
(另有一个Action3参数版本,用于带ChannelId的Action)
Hanlder即消息处理器需要带有两个参数,分别是NetworkConnectionToClient
和消息结构体。
例如:
NetworkServer.RegisterHandler<AddPlayerMessage>(OnServerAddPlayerInternal);
void OnServerAddPlayerInternal(NetworkConnectionToClient conn, AddPlayerMessage msg)
{
}
客户端注册消息Handler
使用:
NetworkClient.RegisterHandler<T>(Action<T> handler, bool requireAuthentication = true);
消息处理器只要一个参数,即消息结构体。例如:
RegisterHandler<SpawnMessage>(OnSpawn);
internal static void OnSpawn(SpawnMessage message){}
注册Handler的时机
- 对于服务器消息Handler一般是在服务器启动时注册。
- 对于客户端消息Handler,有些消息需要在连接之前就注册好Handler。例如NetworkManager.RegisterClientMessages中的SceneMessage。而大多数客户端消息都是在连接之后注册。当然也不能注册太晚,总之要在该类型的第一条消息发送过来之前注册好Handler。
发送消息
客户端向服务器发送消息
使用NetworkConnection
的Send
方法,例如:
connection.Send(new AddPlayerMessage());
服务器向客户端发送消息
使用NetworkConnectionToClient.Send
,由于NetworkConnectionToClient
继承自NetworkConnection
,且它没有改写Send
,因此使用的还是NetworkConnection.Send
。
另外,如果需要向所有的客户端发送消息,可以使用NetworkServer.SendToAll
,其内部实现会遍历所有的NetworkConnectionToClient
,然后调用其Send(ArraySegment<byte> segment, int channelId = Channels.Reliable)
方法,该方法是一个内部方法,当消息被Pack后调用该方法发送,由于SendToAll是发送给多人,因此消息只需要Pack一次,所以直接调用这个方法,而普通的Send方法会在内部进行Pack。
SendToAll
的例子:
public virtual void ServerChangeScene(string newSceneName)
{
if (NetworkServer.active)
{
// notify all clients about the new scene
NetworkServer.SendToAll(new SceneMessage { sceneName = newSceneName });
}
}
小结
本篇中我们介绍了网络消息的定义和使用,下篇中我们将深入消息的底层打包和批量发送机制,并用一个实际案例学习如何使用自定义网络消息。