当前位置:网站首页>Unity C # e-learning (11) -- custom protocol generation tool
Unity C # e-learning (11) -- custom protocol generation tool
2022-06-28 03:54:00 【Handsome_ shuai_】
Unity C# E-learning ( 11、 ... and )—— Custom protocol generation tool
In the development of online games , Agreements are essential , A game may have many agreements , But the agreement is highly repeatable , And both the front end and the back end need , It is obviously unrealistic to complete it manually , Our protocol can be generated through common configuration
One . Protocol profile
- Here the Xml To configure the protocol
<?xml version="1.0" encoding="utf-8"?>
<messages>
<!-- Enumeration type -->
<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 structure type -->
<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 type -->
<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>
Two . Test read Xml The configuration file
private void Start()
{
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(Application.dataPath + "/Src/Lesson32/Lesson32.xml");
XmlNode rootNode = xmlDocument.SelectSingleNode("messages");
if (rootNode == null)
return;
// Read enumeration
XmlNodeList enumNodeList = rootNode.SelectNodes("enum");
if (enumNodeList == null)
return;
foreach (XmlNode enumNode in enumNodeList)
{
if (enumNode?.Attributes == null)
continue;
Debug.Log(" Enumeration name :" + enumNode.Attributes["name"].Value);
Debug.Log(" Namespace :" + 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);
}
}
// Read custom Data Class data
XmlNodeList dataNodeList = rootNode.SelectNodes("data");
if (dataNodeList == null)
return;
foreach (XmlNode dataNode in dataNodeList)
{
if (dataNode?.Attributes == null)
continue;
Debug.Log(" Data name :" + dataNode.Attributes["name"].Value);
Debug.Log(" Namespace :" + 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);
}
}
// Read message type
XmlNodeList msgNodeList = rootNode.SelectNodes("message");
if (msgNodeList == null)
return;
foreach (XmlNode msgNode in msgNodeList)
{
if (msgNode?.Attributes == null)
continue;
Debug.Log(" Message class name :" + msgNode.Attributes["name"].Value);
Debug.Log(" news ID:" + msgNode.Attributes["id"].Value);
Debug.Log(" Namespace :" + msgNode.Attributes["namespace"].Value);
}
}
3、 ... and . according to Enum Configure and generate the corresponding C# Code
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());
}
}
Four . according to Data Configure the generated data class
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");
// Start writing function related
//1. Get byte length function
SetDataLength(sbr, fieldNodeList);
sbr.Append("\r\n");
//2.ToArray() function
SetToArray(sbr, fieldNodeList);
sbr.Append("\r\n");
//3.Reading() function
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. Get byte length function
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() function
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() function
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)";
}
}
5、 ... and . according to Msg Configure to generate corresponding code
- According to the above Data Configuring the generated data class is similar to
- Modify the inherited base class to MsgBase
- modify GetLength() Method , Let the minimum length be 8
- modify ToArray() Method , Write message ID And message length
- Add return MsgID Methods
6、 ... and . test
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 = " This is the test ",
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);
}
边栏推荐
- Pycharm setting pseudo sublime color scheme
- 一文告诉你什么是 Kubernetes
- Li Kou daily question - day 29 -575 Divide candy
- Documentation issues
- uni-app 如何根据环境自动切换请求的地址
- Leetcode: monotonic stack structure (Advanced)
- gcd最大公约数
- 基于 WPF 的酷炫 GUI 窗口的简易实现
- Web APIs DOM-事件基础丨黑马程序员
- What is the difference between slice and array in go question bank 12?
猜你喜欢

音频 scipy 中 spectrogram 的运作机制

django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.13 or newer is required; you have 0.9.3

A Preliminary Study of Blackbody radiation

如何系统学习一门编程语言? | 黑马程序员

Paging query optimization in MySQL of database Series

17 `bs object Node name h3 Parent ` parents get parent node ancestor node

How does the open-ended Hall current sensor help the transformation of DC power distribution?

多线程与高并发四:VarHandle与强软弱虚引用和ThreadLocal

数据库系列之InnoDB中在线DDL实现机制

ambari SSLError: Failed to connect. Please check openssl library versions.
随机推荐
uni-app 如何根据环境自动切换请求的地址
MySQL configuration of database Series F5 load balancing
Detailed explanation of KVM common commands
【毕业季】研究生の毕业总结
MySQL error
STM32外设SDIO和SD卡的配置
小程序的防抖节流怎么写?
「运维有小邓」监控文件及文件夹变更
MySQL 主从复制、分离解析
Introduction to kubernetes resource object and common commands
失联修复:让“躲猫猫”无处可藏
第一章 Bash 入门
数字电路学习笔记(二)
English语法_形容词/副词3级-比较级_常用短语
Arrangement of basic electrical knowledge (I)
TypeScript 联合类型
Change of monitoring documents and folders of "operation and maintenance department"
Paging query optimization in MySQL of database Series
KVM常用命令详解
Lost connection repair: make "hide and seek" nowhere to hide