UN5L2——协议(消息)配置
UN5L2——协议(消息)配置
选择哪种格式配置协议?
利用配置文件配置消息、数据结构、枚举的目的
- 减少工作量,配置一次,之后自动化生成各种语言对应的类文件
- 减少沟通成本,避免前后端语言不同时,手动写代码出现前后端不统一的问题
我们可以选择:xml、json、excel、自定义等等
我们可以根据自己的喜好选择选择方便配置的,好用的即可
配置的主要目的是确定类名、成员变量名
之后根据读取的这些配置信息,再通过代码按照规则自动生成对应的类文件
本课使用xml作为协议配置文件,学会xml配置,其它的方式都是大同小异的
我们主要是学习制作思路和流程,以后的项目中,大家根据自己的喜好选择即可
XML相关知识,可以在数据持久化之XML中进行学习
以xml配置为例 —— 制定配置规则
我们主要执行以下步骤:
-
创建xml配置文件
-
制定配置规则
首先,XML配置规则需要一个根节点,因此我们将
<messages>
作为根节点,其中包裹各个枚举、消息类和枚举的声明1
2
3
<messages>
</messages>-
枚举规则
假设我们要创建这样的枚举
1
2
3
4
5
6
7
8namespace GamePlayer
{
public enum E_PLAYER_TYPE
{
MAIN = 1,
OTHER,
}
}分析得到,要创建这样的枚举,必须要的消息是:枚举所在的命名空间,枚举名,各个字段名,各字段的默认值
因此,我们规定这样的XML配置规则:
- 用
<enum>
节点代表一个枚举的声明,属性包括枚举名和命名空间名,其中包裹若干个字段 - 用
<field>
节点代表一个字段,属性有名字,<field>
可以包裹值,包裹的值即该字段默认值,也可以不包裹值
按照如上规则,可以得到这样的XML配置文本
1
2
3
4
5
6
7
8
<messages>
<!-- 枚举配置规则 -->
<enum name="E_PLAYER_TYPE" namespace="GamePlayer">
<field name="MAIN">2</field>
<field name="OTHER"/>
</enum>
</messages>按照这个规则,我们就再用XML声明一个
E_MONSTER_TYPE
,声明得到:1
2
3
4
5
6
7
8
<messages>
<!-- 枚举配置规则 -->
<enum name="E_MONSTER_TYPE" namespace="GameMonster">
<field name="NORMAL">2</field>
<field name="BOSS"/>
</enum>
</messages> - 用
-
数据类规则
假设我们要创建这样的数据类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27namespace GamePlayer
{
public class PlayerData : BaseData
{
public int id;
public float atk;
public long lev;
public int[] arrays;
public List<int> list;
public Dictionary<int, string> dict;
public override int GetBytesNum()
{
throw new System.NotImplementedException();
}
public override int Reading(byte[] bytes, int beginIndex = 0)
{
throw new System.NotImplementedException();
}
public override byte[] Writeing()
{
throw new System.NotImplementedException();
}
}
}值得一提的是,在配置文件里只需要各个字段(成员变量),而不需要方法,
因为数据类的方法有哪些是固定的,而这些固定方法的具体实现依托于该数据类的各个字段
因此配置文件内只需要各个字段的信息,就可以根据这些字段信息实现数据类的中的固定方法最终分析得到,要创建这样的数据类,必须要的消息是:所在的命名空间,类名,各个字段名,类型,若有泛型则泛型参数是什么
因此,我们规定这样的XML配置规则:
- 用
<data>
节点代表一个数据类的声明,属性包括类名和命名空间名,其中包裹若干个字段 - 用
<field>
节点代表一个字段,属性有名字,变量类型,泛型参数(有泛型的情况下),
<field>
可以包裹值,包裹的值即该字段默认值,也可以不包裹值
按照如上规则,可以得到这样的XML配置文本
1
2
3
4
5
6
7
8
9
10
11
12
13
<messages>
<!-- 数据结构类配置规则 -->
<data name="PlayerData" namespace="GamePlayer">
<field type="int" name="id"/>
<field type="float" name="atk"/>
<field type="bool" name="sex"/>
<field type="long" name="lev"/>
<field type="array" data="int" name="arrays"/>
<field type="list" T="int" name="list"/>
<field type="dic" TKey="int" TValue="string" name="dic"/>
</data>
</messages> - 用
-
消息类规则
假设我们要创建这样的消息类:
1
2
3
4
5
6
7
8
9
10
11
12
13namespace GamePlayer
{
public class PlayerMessage : BaseMessage
{
public int PlayerId;
public PlayerData PlayerData;
public override int GetID()
{
return 1001;
}
}
}所有的消息类都需要有一个自己的消息ID
最终分析得到,要创建这样的数据类,必须要的消息是:所在的命名空间,类名,消息ID,各个字段名,类型等
因此,我们规定这样的XML配置规则:
- 用
<message>
节点代表一个数据类的声明,属性包括消息ID,类名和命名空间名,其中包裹若干个字段 - 用
<field>
节点代表一个字段,属性有名字,变量类型,泛型参数(有泛型的情况下),
<field>
可以包裹值,包裹的值即该字段默认值,也可以不包裹值
按照如上规则,可以得到这样的XML配置文本
1
2
3
4
5
6
7
8
<messages>
<!-- 消息类配置规则 -->
<message id="1001" name="PlayerMsg" namespace="GamePlayer">
<field type="int" name="playerID"/>`
<field type="PlayerData" name="data"/>
</message>
</messages>根据以上规则,我们再声明一个心跳消息ID,因为心跳消息不包含其他数据,因此不需要包裹内容
1
2
3
4
<messages>
<message id="1002" name="HeartMsg" namespace="GameSystem"/>
</messages> - 用
-
读取配置信息
对于XML消息的读取与解析,我们需要调用XmlDocument
读取配置消息的代码如下:
-
首先读取配置文件和根节点
1
2
3XmlDocument xml = new XmlDocument();
xml.Load(Application.dataPath + "/Scripts/L35_协议(消息)配置/Lesson35.xml");
XmlNode root = xml.SelectSingleNode("messages"); -
读取枚举消息
先读取所有的
<enum>
节点,然后遍历,获取其枚举名和命名空间名,
然后读取每个<enum>
节点的所有<field>
节点,然后遍历,读取其名字和值1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16XmlNodeList enumList = root.SelectNodes("enum");
print("**********枚举**********");
foreach (XmlNode enumNode in enumList)
{
print("枚举名:" + enumNode.Attributes["name"].Value);
print("枚举命名空间名:" + enumNode.Attributes["namespace"].Value);
XmlNodeList fields = enumNode.SelectNodes("field");
foreach (XmlNode fieldNode in fields)
{
string str = fieldNode.Attributes["name"].Value;
if (!string.IsNullOrEmpty(fieldNode.InnerText))
str += " = " + fieldNode.InnerText;
str += ", ";
print(str);
}
} -
读取数据类
先读取所有的
<data>
节点,然后遍历,获取其数据类名和命名空间名,
然后读取每个<data>
节点的所有<field>
节点,然后遍历,读取其变量类型,名字1
2
3
4
5
6
7
8
9
10
11
12print("**********数据类**********");
XmlNodeList dataList = root.SelectNodes("data");
foreach (XmlNode dataNode in dataList)
{
print("数据类名:" + dataNode.Attributes["name"].Value);
print("数据类命名空间名:" + dataNode.Attributes["namespace"].Value);
XmlNodeList fields = dataNode.SelectNodes("field");
foreach (XmlNode fieldNode in fields)
{
print(fieldNode.Attributes["type"].Value + " " + fieldNode.Attributes["name"].Value + ";");
}
} -
读取消息类
先读取所有的
<message>
节点,然后遍历,获取其消息类名和命名空间名,以及消息ID
然后读取每个<message>
节点的所有<field>
节点,然后遍历,读取其变量类型,名字1
2
3
4
5
6
7
8
9
10
11
12
13print("**********消息类**********");
XmlNodeList msgList = root.SelectNodes("message");
foreach (XmlNode msgNode in msgList)
{
print("消息类名:" + msgNode.Attributes["name"].Value);
print("消息类命名空间名:" + msgNode.Attributes["namespace"].Value);
print("消息ID:" + msgNode.Attributes["id"].Value);
XmlNodeList fields = msgNode.SelectNodes("field");
foreach (XmlNode fieldNode in fields)
{
print(fieldNode.Attributes["type"].Value + " " + fieldNode.Attributes["name"].Value + ";");
}
}