MessageDispatherComponent
请大家关注我的微博:@NormanLin_BadPixel坏像素
Demo3.0发布后,对MessageDispatherComponent进行了更新,变化其实不大,只是把HotFix相关的代码移除了,大家自己看一下就好。
2018/4/11
对MessageDispatherComponent的实例分析在另外一个笔记里,有兴趣的可以在看完后到 这里 看一下。里面也解释了iMHandler接口的作用。
同样的猜猜先。
“消息调度”。
我之前也学过一点skynet,并且写了一个简单的文档sample gate框架启动流程,所以我大概能猜到这个“消息调度”是干啥的。这里给没了解的同学说一下我自己的理解。
消息调度,调度的是什么消息,又是怎么调度的?其实只要清楚这些,消息调度的作用就很好理解了。
首先,调度的是什么消息。调度的是从网络传过来的数据,服务端就是调度从客户端传过来的消息,客户端则是调度从服务端传过来的消息。
(以帧同步为例)
怎么调度的呢?我们想象一下,我们在客户端上操作,都希望传一些什么数据给服务端呢?大致分一下,1:角色移动。2.角色攻击。就先举这两种数据吧。当我们在客户端这边操作了角色移动和攻击。这时会产生两条指令,msg1和msg2,msg1的type是move,msg2的type是attack,里面的具体信息我们先不用管。这个时候,我们要把这些数据传给服务端。这个时候,消息调度器的第一个作用就出来了,它会把这些信息给统一打包,然后通过自定义的数据类型,把这些信息序列化成json或者protobuff或其他的一些数据,方便网络传输。打好包后,再发送给服务端。
而服务端接收到这些数据,会解析这些数据,这个时候,消息调度器的第二个作用体现出来了,它会用和客户端规定好的数据类型,把序列化的数据再反序列化成对象。然后对这些对象进行一些我们还不知道的处理,比如压入对应的帧队列。同样的,服务端会把当前帧的指令集合发送给客户端,发送的原理跟客户端差不多。而客户端接收到这些消息,同样的会把这些数据反序列化成对象,然后利用消息调度器对消息进行分发处理,这就是消息调度器的第三个作用。比如我们的客户端接收到了自己发送给服务端然后又传回来的msg1和msg2,这个时候,消息调度器会根据msg的type把这些消息交给不同的处理器处理。比如会把type为move的信息交给角色移动组件处理,而把type为attack的消息交给角色攻击组件处理,具体怎么处理就是不同组件需要干的,消息调度器只是起到了一个调度的作用。
(这里的理解是我根据skynet猜的,ET里面的具体是什么作用我们还是得看代码)
看代码
private Dictionary<ushort, List<IMessageMethod>> handlers;
IMessageMethod
void Run(AMessage a);
AMessage
空的类,但是有很多的子类。现在我们不管。
MessageDispatherComponent
foreach (Type type in types)
{
object[] attrs = type.GetCustomAttributes(typeof(MessageHandlerAttribute), false);
if (attrs.Length == 0)
{
continue;
}
MessageHandlerAttribute messageHandlerAttribute = (MessageHandlerAttribute)attrs[0];
IMHandler iMHandler = (IMHandler)Activator.CreateInstance(type);
if (!this.handlers.ContainsKey(messageHandlerAttribute.Opcode))
{
this.handlers.Add(messageHandlerAttribute.Opcode, new List<IMessageMethod>());
}
this.handlers[messageHandlerAttribute.Opcode].Add(new IMessageMonoMethod(iMHandler));
}
这段代码跟之前的EventComponent也很像,无非就是把拥有MessageHandler特性的类向消息调度器注册对应的处理器。因为这个Demo是含热更新的,所以应该有很多脚本是写在热更新层的,所以我们不能通过Shift+F12快速的找到具体都有哪些类拥有MessageHandler特性。那这里也就深究下去了,我们了解一下思路。
public void Handle(MessageInfo messageInfo)
{
List<IMessageMethod> actions;
if (!this.handlers.TryGetValue(messageInfo.Opcode, out actions))
{
Log.Error($"消息 {Opcode2Name.GetName(messageInfo.Opcode)}({messageInfo.Opcode}) 没有处理");
return;
}
foreach (IMessageMethod ev in actions)
{
try
{
ev.Run(messageInfo.Message);
}
catch (Exception e)
{
Log.Error(e.ToString());
}
}
}
瞧,这就是接收到消息后,根据消息的操作符(类型,比如之前说的Move或者Attack)分配给不同的处理器。
MessageInfo
public ushort Opcode { get; set; }
public AMessage Message { get; set; }
public MessageInfo(ushort opcode, AMessage message)
{
this.Opcode = opcode;
this.Message = message;
}
现在终于懂得了,之前说的Opcode就是用来区别不同操作的消息。而不同操作类型的消息里面包含的消息内容肯定也是不一样的,那就是AMessage的派生类需要考虑的咯。
不过这里的消息调度并没有我开头提到的对数据的序列化打包操作,这个操作可能被作者分到其他模块去了吧。
结束语
之前学的东西不见的没用 ———–NormanLin
书到用时方恨少