Unity Mirror 网络消息详解(1)

901

携手创作,共同成长!这是我参与「掘金日新计划 · 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。

发送消息

客户端向服务器发送消息

使用NetworkConnectionSend方法,例如:

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 });
    }
}

小结

本篇中我们介绍了网络消息的定义和使用,下篇中我们将深入消息的底层打包和批量发送机制,并用一个实际案例学习如何使用自定义网络消息。