当前位置:网站首页>Unity C# 网络学习(十一)——自定义协议生成工具
Unity C# 网络学习(十一)——自定义协议生成工具
2022-06-28 03:03:00 【帅_shuai_】
Unity C# 网络学习(十一)——自定义协议生成工具
在开发网络游戏中,协议是必不可少的东西,一款游戏可能有非常多的协议,但是协议的重复性非常高,而且前端后端都需要,人工完成显然不现实,可以通过共同的配置去生成我们的协议
一.协议配置文件
- 这里采用Xml来进行协议的配置
<?xml version="1.0" encoding="utf-8"?>
<messages>
<!--枚举类型-->
<enum name="E_PLAYER_TYPE" namespace="GamePlayer">
<field name="MAIN">1</field>
<field name="OTHER"/>
</enum>
<enum name="E_MONSTER_TYPE" namespace="GameMonster">
<field name="NORMAL">2</field>
<field name="BOSS"/>
</enum>
<!--数据结构类型-->
<data name="PlayerData" namespace="GamePlayer">
<field name="id" type="int"/>
<field name="atk" type="float"/>
<field name="sex" type="bool"/>
<field name="lev" type="long"/>
<field name="arrays" type="array" T="int"/>
<field name="list" type="list" T="int"/>
<field name="dic" type="dic" Tkey="int" TValue="string"/>
</data>
<!--消息类型-->
<message name="PlayerMsg" id="1001" namespace="GamePlayer">
<field name="playerID" type="int" />
<field name="data" type="PlayerData"/>
</message>
<message name="HeartMsg" id="1002" namespace="GameSystem"/>
</messages>
二.测试读取Xml配置文件
private void Start()
{
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(Application.dataPath + "/Src/Lesson32/Lesson32.xml");
XmlNode rootNode = xmlDocument.SelectSingleNode("messages");
if (rootNode == null)
return;
//读取枚举
XmlNodeList enumNodeList = rootNode.SelectNodes("enum");
if (enumNodeList == null)
return;
foreach (XmlNode enumNode in enumNodeList)
{
if (enumNode?.Attributes == null)
continue;
Debug.Log("枚举名称:" + enumNode.Attributes["name"].Value);
Debug.Log("命名空间:" + enumNode.Attributes["namespace"].Value);
XmlNodeList fieldNodeList = enumNode.SelectNodes("field");
if (fieldNodeList == null)
continue;
foreach (XmlNode fieldNode in fieldNodeList)
{
if (fieldNode?.Attributes == null)
continue;
Debug.Log(fieldNode.Attributes["name"].Value);
if (!string.IsNullOrEmpty(fieldNode.InnerText))
Debug.Log(fieldNode.InnerText);
}
}
//读取自定义Data类数据
XmlNodeList dataNodeList = rootNode.SelectNodes("data");
if (dataNodeList == null)
return;
foreach (XmlNode dataNode in dataNodeList)
{
if (dataNode?.Attributes == null)
continue;
Debug.Log("数据名称:" + dataNode.Attributes["name"].Value);
Debug.Log("命名空间:" + dataNode.Attributes["namespace"].Value);
XmlNodeList fieldNodeList = dataNode.SelectNodes("field");
if (fieldNodeList == null)
continue;
foreach (XmlNode fieldNode in fieldNodeList)
{
if (fieldNode?.Attributes == null)
continue;
Debug.Log(fieldNode.Attributes["name"].Value);
Debug.Log(fieldNode.Attributes["type"].Value);
}
}
//读取消息类型
XmlNodeList msgNodeList = rootNode.SelectNodes("message");
if (msgNodeList == null)
return;
foreach (XmlNode msgNode in msgNodeList)
{
if (msgNode?.Attributes == null)
continue;
Debug.Log("消息类名称:" + msgNode.Attributes["name"].Value);
Debug.Log("消息ID:" + msgNode.Attributes["id"].Value);
Debug.Log("命名空间:" + msgNode.Attributes["namespace"].Value);
}
}
三.根据Enum配置生成对应的C#代码
public void GenerateEnum(XmlNodeList nodeList)
{
if (nodeList == null)
return;
StringBuilder sbr = new StringBuilder();
foreach (XmlNode enumNode in nodeList)
{
sbr.Clear();
if (enumNode?.Attributes == null)
continue;
string className = enumNode.Attributes["name"].Value;
string namespaceName = enumNode.Attributes["namespace"].Value;
XmlNodeList fieldNodeList = enumNode.SelectNodes("field");
if (fieldNodeList == null)
continue;
sbr.Append($"namespace {
namespaceName}\r\n");
sbr.Append("{\r\n");
sbr.Append($"\tpublic enum {
className}\r\n");
sbr.Append("\t{\r\n");
foreach (XmlNode fieldNode in fieldNodeList)
{
if (fieldNode?.Attributes == null)
continue;
string fieldName = fieldNode.Attributes["name"].Value;
sbr.Append("\t\t");
if (string.IsNullOrEmpty(fieldNode.InnerText))
sbr.Append(fieldName);
else
{
sbr.Append(fieldName);
sbr.Append(" = ");
sbr.Append(fieldNode.InnerText);
}
sbr.Append(",\r\n");
}
sbr.Append("\t}\r\n");
sbr.Append("}\r\n");
string path = SavePath + namespaceName + "/Enum";
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
using StreamWriter sw = new StreamWriter(path + "/" + className + ".cs");
sw.Write(sbr.ToString());
}
}
四.根据Data配置生成数据类
public void GenerateData(XmlNodeList nodeList)
{
if (nodeList == null)
return;
StringBuilder sbr = new StringBuilder();
foreach (XmlNode dataNode in nodeList)
{
sbr.Clear();
if (dataNode?.Attributes == null)
continue;
string className = dataNode.Attributes["name"].Value;
string namespaceName = dataNode.Attributes["namespace"].Value;
XmlNodeList fieldNodeList = dataNode.SelectNodes("field");
if (fieldNodeList == null)
continue;
sbr.Append("using System.Collections.Generic;\r\n");
sbr.Append("using System.Text;\r\n");
sbr.Append($"namespace {
namespaceName}\r\n");
sbr.Append("{\r\n");
sbr.Append($"\tpublic class {
className} : BaseData\r\n");
sbr.Append("\t{\r\n");
foreach (XmlNode fieldNode in fieldNodeList)
{
if (fieldNode?.Attributes == null)
continue;
string fieldName = fieldNode.Attributes["name"].Value;
string typeName = fieldNode.Attributes["type"].Value;
sbr.Append("\t\tpublic ");
if (typeName == "array")
{
string t = fieldNode.Attributes["T"].Value;
sbr.Append($"{
t}[] {
fieldName}");
}
else if (typeName == "list")
{
string t = fieldNode.Attributes["T"].Value;
sbr.Append($"List<{
t}> {
fieldName}");
}
else if (typeName == "dic")
{
string key = fieldNode.Attributes["Tkey"].Value;
string val = fieldNode.Attributes["TValue"].Value;
sbr.Append($"Dictionary<{
key},{
val}> {
fieldName}");
}
else if (typeName == "enum")
{
string t = fieldNode.Attributes["T"].Value;
sbr.Append($"{
t} {
fieldName}");
}
else
{
sbr.Append($"{
typeName} {
fieldName}");
}
sbr.Append(";\r\n");
}
sbr.Append("\r\n");
//开始写入函数相关
//1.获取字节长度函数
SetDataLength(sbr, fieldNodeList);
sbr.Append("\r\n");
//2.ToArray()函数
SetToArray(sbr, fieldNodeList);
sbr.Append("\r\n");
//3.Reading()函数
SetReading(sbr, fieldNodeList);
sbr.Append("\r\n");
sbr.Append("\t}\r\n");
sbr.Append("}\r\n");
string path = SavePath + namespaceName + "/Data";
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
using StreamWriter sw = new StreamWriter(path + "/" + className + ".cs");
sw.Write(sbr.ToString());
}
}
1.获取字节长度函数
private void SetDataLength(StringBuilder sbr, XmlNodeList fieldNodeList)
{
sbr.Append("\t\tpublic override int GetLength()\r\n");
sbr.Append("\t\t{\r\n");
sbr.Append("\t\t\tint num = 0;\r\n");
foreach (XmlNode fieldNode in fieldNodeList)
{
if (fieldNode?.Attributes == null)
continue;
string type = fieldNode.Attributes["type"].Value;
string name = fieldNode.Attributes["name"].Value;
sbr.Append("\t\t\t");
switch (type)
{
case "array":
string arrT = fieldNode.Attributes["T"].Value;
sbr.Append("num += 4;\r\n");
sbr.Append($"\t\t\tforeach ({
arrT} t in {
name})\r\n");
sbr.Append($"\t\t\t\tnum += {
GetLength(arrT, "t")};\r\n");
break;
case "list":
string listT = fieldNode.Attributes["T"].Value;
sbr.Append("num += 4;\r\n");
sbr.Append($"\t\t\tforeach ({
listT} t in {
name})\r\n");
sbr.Append($"\t\t\t\tnum += {
GetLength(listT, "t")};\r\n");
break;
case "dic":
string keyT = fieldNode.Attributes["Tkey"].Value;
string valueT = fieldNode.Attributes["TValue"].Value;
sbr.Append("num += 4;\r\n");
sbr.Append($"\t\t\tforeach ({
keyT} key in {
name}.Keys)\r\n");
sbr.Append("\t\t\t{\r\n");
sbr.Append($"\t\t\t\tnum += {
GetLength(keyT, "key")};\r\n");
sbr.Append($"\t\t\t\tnum += {
GetLength(valueT, name + "[key]")};\r\n");
sbr.Append("\t\t\t}\r\n");
break;
default:
sbr.Append(
$"num += {
GetLength(type, name)};\r\n");
break;
}
}
sbr.Append("\t\t\treturn num;\r\n");
sbr.Append("\t\t}\r\n");
}
private string GetLength(string type, string name)
{
switch (type)
{
case "int":
case "float":
case "enum":
return "4";
case "bool":
case "byte":
return "1";
case "long":
return "8";
case "short":
return "2";
case "string":
return $"(4 + Encoding.UTF8.GetBytes({
name}).Length)";
default:
return $"{
name}.GetLength()";
}
}
2.ToArray()函数
private void SetToArray(StringBuilder sbr, XmlNodeList fieldNodeList)
{
sbr.Append("\t\tpublic override byte[] ToArray()\r\n");
sbr.Append("\t\t{\r\n");
sbr.Append("\t\t\tint index = 0;\r\n");
sbr.Append("\t\t\tbyte[] buffer = new byte[GetLength()];\r\n");
foreach (XmlNode fieldNode in fieldNodeList)
{
if (fieldNode?.Attributes == null)
continue;
string name = fieldNode.Attributes["name"].Value;
string type = fieldNode.Attributes["type"].Value;
switch (type)
{
case "enum":
sbr.Append($"\t\t\tWriteInt(buffer,(int){
name},ref index);\r\n");
break;
case "array":
string arrT = fieldNode.Attributes["T"].Value;
sbr.Append($"\t\t\tWriteInt(buffer,{
name}.Length,ref index);\r\n");
sbr.Append($"\t\t\tforeach ({
arrT} t in {
name})\r\n");
sbr.Append($"\t\t\t\t{
GetWriteFuncString(arrT, "t")}\r\n");
break;
case "list":
string listT = fieldNode.Attributes["T"].Value;
sbr.Append($"\t\t\tWriteInt(buffer,{
name}.Count,ref index);\r\n");
sbr.Append($"\t\t\tforeach ({
listT} t in {
name})\r\n");
sbr.Append($"\t\t\t\t{
GetWriteFuncString(listT, "t")}\r\n");
break;
case "dic":
string keyT = fieldNode.Attributes["Tkey"].Value;
string valueT = fieldNode.Attributes["TValue"].Value;
sbr.Append($"\t\t\tWriteInt(buffer,{
name}.Count,ref index);\r\n");
sbr.Append($"\t\t\tforeach ({
keyT} key in {
name}.Keys)\r\n");
sbr.Append("\t\t\t{\r\n");
sbr.Append($"\t\t\t\t{
GetWriteFuncString(keyT, "key")}\r\n");
sbr.Append($"\t\t\t\t{
GetWriteFuncString(valueT, name+"[key]")}\r\n");
sbr.Append("\t\t\t}\r\n");
break;
default:
sbr.Append($"\t\t\t{
GetWriteFuncString(type, name)}\r\n");
break;
}
}
sbr.Append("\t\t\treturn buffer;\r\n");
sbr.Append("\t\t}\r\n");
}
private string GetWriteFuncString(string type,string name)
{
switch (type)
{
case "enum":
return $"WriteInt(buffer,(int){
name},ref index);";
case "int":
return $"WriteInt(buffer,{
name},ref index);";
case "float":
return $"WriteFloat(buffer,{
name},ref index);";
case "bool":
return $"WriteBool(buffer,{
name},ref index);";
case "long":
return $"WriteLong(buffer,{
name},ref index);";
case "string":
return $"WriteString(buffer,{
name},ref index);";
case "short":
return $"WriteShort(buffer,{
name},ref index);";
default:
return $"WriteData(buffer,{
name},ref index);";
}
}
3.SetReading()函数
private void SetReading(StringBuilder sbr, XmlNodeList fieldNodeList)
{
sbr.Append("\t\tpublic override int Reading(byte[] bytes, int startIndex = 0)\r\n");
sbr.Append("\t\t{\r\n");
sbr.Append("\t\t\tint index = startIndex;\r\n");
foreach (XmlNode fieldNode in fieldNodeList)
{
if (fieldNode?.Attributes == null)
continue;
string name = fieldNode.Attributes["name"].Value;
string type = fieldNode.Attributes["type"].Value;
sbr.Append("\t\t\t");
switch (type)
{
case "enum":
string enumT = fieldNode.Attributes["T"].Value;
sbr.Append($"{
name} = ({
enumT})ReadInt(bytes, ref index);\r\n");
break;
case "array":
string arrayT = fieldNode.Attributes["T"].Value;
sbr.Append($"{
name} = new {
arrayT}[ReadInt(bytes, ref index)];\r\n");
sbr.Append($"\t\t\tfor (int i = 0; i < {
name}.Length; i++)\r\n");
sbr.Append($"\t\t\t\t{
name}[i] = {
GetReadFuncString(arrayT)};\r\n");
break;
case "list":
string listT = fieldNode.Attributes["T"].Value;
sbr.Append($"{
name} = new List<{
listT}>(ReadInt(bytes, ref index));\r\n");
sbr.Append($"\t\t\tfor (int i = 0; i < {
name}.Capacity; i++)\r\n");
sbr.Append($"\t\t\t\t{
name}.Add({
GetReadFuncString(listT)});\r\n");
break;
case "dic":
string keyT = fieldNode.Attributes["Tkey"].Value;
string valueT = fieldNode.Attributes["TValue"].Value;
sbr.Append($"int {
name}Count = ReadInt(bytes, ref index);\r\n");
sbr.Append($"\t\t\t{
name} = new Dictionary<{
keyT},{
valueT}>({
name}Count);\r\n");
sbr.Append($"\t\t\tfor (int i = 0;i < {
name}Count; i++)\r\n");
sbr.Append($"\t\t\t\t{
name}[{
GetReadFuncString(keyT)}] = {
GetReadFuncString(valueT)};\r\n");
break;
default:
sbr.Append($"{
name} = {
GetReadFuncString(type)};\r\n");
break;
}
}
sbr.Append("\t\t\treturn index - startIndex;\r\n");
sbr.Append("\t\t}\r\n");
}
private string GetReadFuncString(string type)
{
switch (type)
{
case "int":
return "ReadInt(bytes, ref index)";
case "float":
return "ReadFloat(bytes, ref index)";
case "bool":
return "ReadBool(bytes, ref index)";
case "long":
return "ReadLong(bytes, ref index)";
case "string":
return "ReadString(bytes, ref index)";
case "short":
return "ReadShort(bytes, ref index)";
default:
return $"ReadData<{
type}>(bytes, ref index)";
}
}
五.根据Msg配置生成对应代码
- 与上面的根据Data配置生成数据类类似
- 修改继承的基类为MsgBase
- 修改GetLength()方法,让最小长度为8
- 修改ToArray()方法,写入消息ID和消息长度
- 新增返回MsgID的方法
六.测试
private void Start()
{
GamePlayer.PlayerData playerData = new GamePlayer.PlayerData
{
id = 1000,
atk = 41.45f,
sex = false,
lev = 6666666666,
playerType = E_PLAYER_TYPE.OTHER,
arrays = new[] {
1, 2, 3, 4, 5, 6},
list = new List<int> {
1, 3, 5, 7, 9},
dic = new Dictionary<int, string>
{
{
1, "zzs"},
{
2, "ywj"}
},
hp = 100,
itemList = new List<string>{
"zzs","wy","lzq"},
name = "zzs",
info = "这是测试",
monsterDic = new Dictionary<string, string>
{
{
"zzs","ywj"}
},
homeArr = new []{
3.14f,6.6789f},
moneyData = new MoneyData
{
money = 99999,
moneyArr = new []{
1,2,3},
moneyDic = new Dictionary<string, int>{
{
"zzs",1000}},
moneyList = new List<int>{
1,2,3,4}
}
};
GamePlayer.PlayerMsg playerMsg = new GamePlayer.PlayerMsg
{
playerID = 100001,
data = playerData
};
byte[] buffer = playerMsg.ToArray();
int index = 0;
int msgId = BitConverter.ToInt32(buffer,index);
index += 4;
int length = BitConverter.ToInt32(buffer, index);
index += 4;
GamePlayer.PlayerMsg playerMsg1 = new GamePlayer.PlayerMsg();
playerMsg1.Reading(buffer, index);
}
边栏推荐
- 从遇见大咖到成为大咖,昇腾AI开发者创享日给开发者带来无限可能
- Sublime text 3 basic configuration tutorial
- A pit filling trip based on LNMP to build a personal website
- 力扣每日一题-第29天-219.存在重复元素Ⅱ
- View the SQL execution plan according to explain and optimize the SQL
- 用于 C# 的 SQL 基本语法总结
- 17 `bs对象.节点名h3.parent` parents 获取父节点 祖先节点
- Set drop-down options on Excel files
- Documentation issues
- 在牛客中使用JS编程题【split】
猜你喜欢
Why is the service implementation class always red
数据库系列之MySQL中的执行计划
Lost connection repair: make "hide and seek" nowhere to hide
Win 10出现bitlocke恢复,蓝屏错误代码0x1600007e
Anaconda command usage
解析STEAM教育框架下未来教师研究能力
matlab习题 —— 数据的基本处理
Scalable storage system (I)
"Five layer" architecture of cloud applications and services
如何系统学习一门编程语言? | 黑马程序员
随机推荐
力扣每日一题-第29天-1491.去掉最低工资和最高工资后的平均工资
根据Explain查看sql执行计划,对SQL进行优化
How to automatically add author, time, etc. to eclipse
View the SQL execution plan according to explain and optimize the SQL
机器人编程教育的市场竞争力
Import an excel file, solve the problem of skipping blank cells without reading and moving the subscript forward, and return_ BLANK_ AS_ Null red
电子地图坐标系统研究整理
WPF 下的自定义控件以及 Grid 中控件的自适应
第九章 APP项目测试(3) 测试工具
WARN:&nbsp;SQL&nbsp;Error:&nbsp;…
nn. Parameter and torch nn. Init series of functions to initialize model parameters
Automatic backup of MySQL database
基于 WPF 的酷炫 GUI 窗口的简易实现
matlab习题 —— 数据的基本处理
Dataloader parameter collate_ Use of FN
多线程与高并发三:AQS底层源码分析及其实现类
Simple implementation of cool GUI window based on WPF
数据库系列之InnoDB中在线DDL实现机制
Resource management, high availability and automation (Part 2)
No&nbsp; result&nbsp; defined&amp; nbsp…