ET服务器框架学习笔记(六)
文章目录
OpcodeTypeComponent
接下来开始记录下ET的通讯相关的系统,这个系统比较庞大,估计要分好多篇文章记录,一点一点来吧。
一、OpcodeTypeComponent是什么?
我们知道通讯肯定要有通讯的数据(貌似是废话),通讯最底层是二进制的流,我们通讯的方式主要就是将我们认识的数据结构,变成二进制,发到目的地,然后再将二进制还原的过程,具体可以看下TCP/IP的七层通讯协议。
既然协议需要提前订好,那么通讯需要的数据结构也得提前定义好,要不然怎么知道对方发过来的是个什么东西呢。
OpcodeTypeComponent组件的功能就是管理所有通信需要用到的协议数据结构。他将所有定义好的通讯数据结构全部放到自己内部,进行统一管理,这样方便查询,序列化与反序列化数据结构。我们知道ET管理类的方式主要是利用特性与反射,这里也不例外,我们先看一个例子:
[Message(InnerOpcode.M2M_TrasferUnitRequest)]
public partial class M2M_TrasferUnitRequest: IRequest
{
public int RpcId { get; set; }
public Unit Unit { get; set; }
}
重点看特性:[Message(InnerOpcode.M2M_TrasferUnitRequest)],其中InnerOpcode.M2M_TrasferUnitRequest是一个枚举,也就是协议号,可以理解为一个id对应一个数据结构。
接下来看看OpcodeTypeComponentLoadSystem,根据之前的经验,直接看代码:
[ObjectSystem]
public class OpcodeTypeComponentLoadSystem : LoadSystem<OpcodeTypeComponent>
{
public override void Load(OpcodeTypeComponent self)
{
self.Load();
}
}
即当Game.Scene.AddComponent();之后,会调用OpcodeTypeComponentLoadSystem的Load,然后调用到OpcodeTypeComponent的Load。先不看Load里面做了啥,先看看OpcodeTypeComponent有什么数据。
二、OpcodeTypeComponent的数据
1.管理的数据
- DoubleMap<ushort, Type> opcodeTypes:DoubleMap又是一个ET封装的数据结构,他的代码比较简单,就是一个双向KV的字典,内部还封装了转成List的各种方法。
- Dictionary<ushort, object> typeMessages:字典类,从定义上看应该是一个协议ID对应一个object的。
接下来再看LOAD代码,从而分析出这两个结构主要是用来管理什么内容的
2.Load()
代码如下(示例):
public void Load()
{
this.opcodeTypes.Clear();
this.typeMessages.Clear();
List<Type> types = Game.EventSystem.GetTypes(typeof(MessageAttribute));
foreach (Type type in types)
{
object[] attrs = type.GetCustomAttributes(typeof(MessageAttribute), false);
if (attrs.Length == 0)
{
continue;
}
MessageAttribute messageAttribute = attrs[0] as MessageAttribute;
if (messageAttribute == null)
{
continue;
}
this.opcodeTypes.Add(messageAttribute.Opcode, type);
this.typeMessages.Add(messageAttribute.Opcode, Activator.CreateInstance(type));
}
}
代码比较简短,写法和之前的EventSystem比较类似。可以看到主要内容:
- 清理自身的两个数据结构内的所有数据
- 从EventSystem中拿到所有带有MessageAttribute标签特性的类信息,对于上面的例子就是拿到了M2M_TrasferUnitRequest类的类型信息、
- 遍历所有的类型信息,进行一些基础判定,然后将对应的协议号与类型信息都存入到this.opcodeTypes中进行管理,
this.opcodeTypes.Add(messageAttribute.Opcode, type);
- 实例化对应的类型,并将其与协议号一一对应,存入this.typeMessages进行管理,
this.typeMessages.Add(messageAttribute.Opcode, Activator.CreateInstance(type));
经过上述步骤后,知道opcodeTypes存放的是协议号与协议类型的类型信息,typeMessages存放的是协议号与实例化的协议类型实例。
有了这两个数据结构之后,我们能很方便的从协议类型-协议号-协议类型信息,之间进行转换。
# 三、OpcodeTypeComponent的方法 OpcodeTypeComponent相关的方法都比较简单,都是一些查找方法: - 通过类型信息获取协议号:
public ushort GetOpcode(Type type)
{
return this.opcodeTypes.GetKeyByValue(type);
}
- 通过协议号获取类型信息:
public Type GetType(ushort opcode)
{
return this.opcodeTypes.GetValueByKey(opcode);
}
- 通过协议号获取类型实例
public object GetInstance(ushort opcode)
{
#if SERVER
Type type = this.GetType(opcode);
if (type == null)
{
// 服务端因为有人探测端口,有可能会走到这一步,如果找不到opcode,抛异常
throw new Exception($"not found opcode: {opcode}");
}
return Activator.CreateInstance(type);
#else
return this.typeMessages[opcode];
#endif
}
上面这里需要注意的地方在于,区别了客户端和服务器。客户端利用了上面的typeMessages,缓存了协议数据类型,实现0GC,而服务器由于协议跨协程(源代码写的),我个人认为是会跨进程通信,所以反射实例化一个协议数据类。
总结
这篇主要记录的是ET通信里面的基础协议数据类如果管理,下篇应该是消息转发,派发相关内容。