Unity C# 网络学习(十一)——自定义协议生成工具

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);
    }

猜你喜欢

转载自blog.csdn.net/zzzsss123333/article/details/125477604
今日推荐