在开发网络游戏中,协议是必不可少的东西,一款游戏可能有非常多的协议,但是协议的重复性非常高,而且前端后端都需要,人工完成显然不现实,可以通过共同的配置去生成我们的协议
<messages>
<enum name="E_PLAYER_TYPE" namespace="GamePlayer">
<field name="MAIN">1field>
<field name="OTHER"/>
enum>
<enum name="E_MONSTER_TYPE" namespace="GameMonster">
<field name="NORMAL">2field>
<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>
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);
}
}
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());
}
}
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());
}
}
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()";
}
}
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);";
}
}
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)";
}
}
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);
}