写在前面
这个可能发github上交issue会更好,但是最近的issue感觉都很久没人理了,想了想还是发论坛了
首先来讲讲发生了什么
在启动服务器时发生以下报错:
System.Collections.Generic.KeyNotFoundException: The given key 'ET.****' was not present in the dictionary.
at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at ET.EventSystem.GetType(String typeName) in ****\Unity\Codes\Model\Core\Object\EventSystem.cs:line 207
at ET.OpcodeTypeComponent.Awake() in ****\Unity\Codes\Model\Module\Message\OpcodeTypeComponent.cs:line 82
at ET.OpcodeTypeComponentAwakeSystem.Awake(OpcodeTypeComponent self) in ****\Unity\Codes\Model\Module\Message\OpcodeTypeComponent.cs:line 12
at ET.AwakeSystem`1.Run(Object o) in ****\Unity\Codes\Model\Core\Object\IAwakeSystem.cs:line 65
at ET.EventSystem.Awake(Entity component) in ****\Unity\Codes\Model\Core\Object\EventSystem.cs:line 362
产生原因
原因是之前修改了一下协议导出工具,以允许根据proto文件的目录生成多文件代码。同时,导出的命名空间也从proto的文件中读取而不是固定使用ET,以此和ET的代码区分开来。(几十几百个协议写一个proto里感觉是种灾难)
然后就发现了这个问题。以下是OpcodeTypeComponent部分的源码:
this.requestResponse.Add(type, Game.EventSystem.GetType($"ET.{responseTypeAttribute.Type}"));
这里读取了ResponseTypeAttribute然后把请求与相应关联,然而这里使用的Type是取自ResponseTypeAttribute标签参数中的nameof(Type),也就导致了这个地方其实根本就不知道对应类的FullName(寄),以下给出源码中的协议生成写法:
[ResponseType(nameof(****))] // <- 有问题的其实就是这一行
[Message(****.****)] // <-这一段在Tools中做了一些拓展,允许了多文件消息存在
[ProtoContract]
public partial class ****: Object, IRequest {...}
一种解决方案
既然Game.EventSystem中使用的是FullName作为检索,那么把[ResponseType(nameof(****))]修改为[ResponseType(typeof(****))]就能解决第一个问题。然后就是考虑空间,我不知道存string和存type哪个对IL更友好,所以先保留原文使用string的做法,有知道的大哥还请给个提示。
以下是修改方案, 涉及几个文件
OpcodeTypeComponent
首先是OpcodeTypeComponent.cs这个文件(地址\Unity\Codes\Model\Module\Message\OpcodeTypeComponent.cs)
将
this.requestResponse.Add(type, Game.EventSystem.GetType($"ET.{responseTypeAttribute.Type}"));
改为
this.requestResponse.Add(type, Game.EventSystem.GetType(responseTypeAttribute.Type));
ResponseTypeAttribute
然后是 ResponseTypeAttribute.cs ,和上一个文件在同一目录下
将
public ResponseTypeAttribute(string type)
{
this.Type = type;
}
改为
public ResponseTypeAttribute(Type type)
{
this.Type = type.FullName;
}
Proto2CS
然后是Tools下的Proto2CS.cs文件,(地址:\Tools\App\Apps\Proto2CS\Proto2CS.cs)
将
if (newline.StartsWith("//ResponseType"))
{
string responseType = line.Split(" ")[1].TrimEnd('\r', '\n');
sb.AppendLine($"\t[ResponseType(nameof({responseType}))]");
continue;
}
改为
if (newline.StartsWith("//ResponseType"))
{
string responseType = line.Split(" ")[1].TrimEnd('\r', '\n');
sb.AppendLine($"\t[ResponseType(typeof({responseType}))]");
continue;
}
这样就算全部搞定了
题外话
- tools生成的代码中有许多的尾行和分隔符问题,强烈建议自己在生成最后把所有的’\t’强行替换为自己编码规范中的分隔符(比如四个空格),并且把Append(xxx\n)改为AppendLine(xxx),这样尾行和分隔符才能统一
- 因为还没有仔细看配置相关的内容,所以只是粗略看了一下配置似乎有同样的问题,但是这部分demo中几乎没有用到反射获取配置(robot有一例,但是不是核心类,基本不影响),有类似问题的话还请注意。