概述
接Unity网络开发基础
导入基础知识中的代码
需求分析
手动写Handler类
手动书写消息池
using GamePlayer;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 消息池中 主要是用于 注册 ID和消息类型以及消息处理器类型的映射关系
/// 方便我们获取对象 进行反序列化和消息逻辑处理
/// </summary>
public class MsgPool
{//记录消息类型和ID的映射关系private Dictionary<int, Type> messages = new Dictionary<int, Type>();//记录消息处理器类型和ID的映射关系private Dictionary<int, Type> handlers = new Dictionary<int, Type>();public MsgPool(){//在构造函数中进行 注册 注册映射关系Register(1001, typeof(PlayerMsg), typeof(PlayerMsgHandler));//后面继续添加消息类}private void Register(int id, Type messageType, Type handlerType){messages.Add(id, messageType);handlers.Add(id, handlerType);}/// <summary>/// 根据ID 得到一个指定的消息类对象/// </summary>/// <param name="id"></param>/// <returns></returns>public BaseMsg GetMessage(int id){if (!messages.ContainsKey(id))return null;return Activator.CreateInstance(messages[id]) as BaseMsg;}/// <summary>/// 根据ID 得到一个指定的消息处理类对象/// </summary>/// <param name="id"></param>/// <returns></returns>public BaseHandler GetHandler(int id){if (!handlers.ContainsKey(id))return null;return Activator.CreateInstance(handlers[id]) as BaseHandler;}}
工具生成Handler
工具生成消息池
源码
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using UnityEngine;namespace GamePlayer
{public enum E_Player_Type{Main,Other,}public class PlayerTest : BaseData{public List<int> list;public Dictionary<int, string> dic;public int[] arrays;public E_Player_Type type;public PlayerData player;public override int GetByteNum(){int num = 0;num += 4; //list.Countfor (int i = 0; i < list.Count; i++){num += 4;}num += 4; //dic.Countforeach (int key in dic.Keys){num += 4;//key所占的字节数num += 4;//value 字符串长度 占的字节数num += Encoding.UTF8.GetByteCount(dic[key]);}num += 4; //arrays.Length 数组长度for (int i = 0; i < arrays.Length; i++){num += 4;}num += 4; //枚举 用int来存num += player.GetByteNum(); //PlayerDatareturn num;}public override int Reading(byte[] bytes, int beginIndex = 0){int index = beginIndex;//反序列化 listlist = new List<int>();short listCount = ReadShort(bytes, ref index); for (int i = 0; i < listCount; i++){list.Add(ReadInt(bytes, ref index));}//dicdic = new Dictionary<int, string>();short dicCount = ReadShort(bytes, ref index); for (int i = 0; i < dicCount; i++){dic.Add(ReadInt(bytes, ref index), ReadString(bytes, ref index));}//arraysint arrayLength = ReadShort(bytes, ref index);arrays = new int[arrayLength];for (int i = 0; i < arrayLength; i++){arrays[i] = ReadInt(bytes, ref index);}//枚举type = (E_Player_Type)ReadInt(bytes, ref index);//自定义类型player = ReadData<PlayerData>(bytes, ref index);return index - beginIndex;}public override byte[] Writing(){//固定内容int index = 0;byte[] bytes = new byte[GetByteNum()];//可变的 是根据成员变量来决定如何拼接的//存储 list的长度WriteShort(bytes, (short)list.Count, ref index);for (int i = 0; i < list.Count; i++){WriteInt(bytes, list[i], ref index);}//存储 dic//先长度WriteShort(bytes, (short)dic.Count, ref index);foreach (int key in dic.Keys){WriteInt(bytes, key, ref index);WriteString(bytes, dic[key], ref index);}//存储 数组WriteShort(bytes, (short)arrays.Length, ref index);for (int i = 0; i < arrays.Length; i++){WriteInt(bytes, arrays[i], ref index);}//存储 枚举 (我们用一个 int 来存储枚举)type 是一个枚举类型WriteInt(bytes, Convert.ToInt32(type), ref index);//存储 自定义数据结构类WriteData(bytes, player, ref index);//固定内容return bytes;}}
}public class GenerateCSharp
{//协议保存路径private string SAVE_PATH = Application.dataPath + "/Scripts/Protocol/";//生成枚举public void GenerateEnum(XmlNodeList nodes){//生成枚举脚本的逻辑string namespaceStr = "";string enumNameStr = "";string fieldStr = "";foreach (XmlNode enumNode in nodes){//获取命名空间配置信息namespaceStr = enumNode.Attributes["namespace"].Value;//获取枚举名配置信息enumNameStr = enumNode.Attributes["name"].Value;//获取所有的字段节点 然后进行字符串拼接XmlNodeList enumFields = enumNode.SelectNodes("field");//清空上一次的数据fieldStr = "";foreach (XmlNode enumField in enumFields){fieldStr += "\t\t" + enumField.Attributes["name"].Value;if (enumField.InnerText != "")fieldStr += " = " + enumField.InnerText;fieldStr += ",\r\n";}//对所有可变的内容进行拼接string enumStr = $"namespace {namespaceStr}\r\n" + "{\r\n" + $"\tpublic enum {enumNameStr}\r\n" +"\t{\r\n" +$"{fieldStr}" + "\t}\r\n" + "}";//保存文件的路径string path = SAVE_PATH + namespaceStr + "/Enum/";//如果不存在该文件夹 则创建if (!Directory.Exists(path)){Directory.CreateDirectory(path);}//字符串保存 存储为枚举脚本文件//参数一:文件名//参数二:转换成string 的 数据File.WriteAllText(path + enumNameStr + ".cs", enumStr);}Debug.Log("枚举生成结束");}//生成数据结构类public void GenerateData(XmlNodeList nodes){string namespaceStr = "";string classNameStr = "";string fieldStr = "";//将 GetBytesNum方法 写成字符串string getByteNumStr = "";// 将 Writing方法 写成字符串string writingStr = "";//将 Reading方法 写成字符串string readingStr = "";foreach (XmlNode dataNode in nodes){//命名空间namespaceStr = dataNode.Attributes["namespace"].Value;//类名classNameStr = dataNode.Attributes["name"].Value;//读取所有字段节点XmlNodeList fields = dataNode.SelectNodes("field");//通过这个方法进行成员变量声明的拼接 返回拼接结果fieldStr = GetFieldStr(fields);//通过某个方法 对GetBytesNum函数中的字符串内容进行拼接 返回结果getByteNumStr = GetGetBytesNumStr(fields);//通过某个方法 对Writing函数中的字符串内容进行拼接 返回结果writingStr = GetWritingStr(fields);//通过某个方法 对Reading函数中的字符串内容进行拼接 返回结果readingStr = GetReadingStr(fields);string dataStr = "using System.Collections.Generic;\r\n" +"using System.Text;\r\n" +"using System;\r\n\r\n" + $"namespace {namespaceStr}\r\n" +"{\r\n" +$"\tpublic class {classNameStr} : BaseData\r\n" +"\t{\r\n" +$"{fieldStr}\r\n" + "\t\tpublic override int GetByteNum()\r\n" + "\t\t{\r\n" + "\t\t\tint num = 0;\r\n" + $"{getByteNumStr}" + "\t\t\treturn num;\r\n" + "\t\t}\r\n\r\n" + "\t\tpublic override byte[] Writing()\r\n" + "\t\t{\r\n" + "\t\t\tint index = 0;\r\n" + "\t\t\tbyte[] bytes = new byte[GetByteNum()];\r\n" + $"{writingStr}" + "\t\t\treturn bytes;\r\n" + "\t\t}\r\n\r\n" +"\t\tpublic override int Reading(byte[] bytes, int beginIndex = 0)\r\n" +"\t\t{\r\n" +"\t\t\tint index = beginIndex;\r\n" + $"{readingStr}" +"\t\t\treturn index - beginIndex;\r\n" +"\t\t}\r\n\r\n" +"\t}\r\n" +"}";//保存为 脚本对象//保存文件的路径string path = SAVE_PATH + namespaceStr + "/Data/";//如果没有该路径 就创建if (!Directory.Exists(path))Directory.CreateDirectory(path);//字符串保存 存储为数据结构类脚本File.WriteAllText(path + classNameStr + ".cs", dataStr);}Debug.Log("数据结构类生成结束");}//生成消息类public void CenerateMsg(XmlNodeList nodes){string namespaceStr = "";string classNameStr = "";string idStr = "";string fieldStr = "";//将 GetBytesNum方法 写成字符串string getByteNumStr = "";// 将 Writing方法 写成字符串string writingStr = "";//将 Reading方法 写成字符串string readingStr = "";foreach (XmlNode dataNode in nodes){//命名空间namespaceStr = dataNode.Attributes["namespace"].Value;//类名classNameStr = dataNode.Attributes["name"].Value;//消息ididStr = dataNode.Attributes["id"].Value;//读取所有字段节点XmlNodeList fields = dataNode.SelectNodes("field");//通过这个方法进行成员变量声明的拼接 返回拼接结果fieldStr = GetFieldStr(fields);//通过某个方法 对GetBytesNum函数中的字符串内容进行拼接 返回结果getByteNumStr = GetGetBytesNumStr(fields);//通过某个方法 对Writing函数中的字符串内容进行拼接 返回结果writingStr = GetWritingStr(fields);//通过某个方法 对Reading函数中的字符串内容进行拼接 返回结果readingStr = GetReadingStr(fields);string dataStr = "using System.Collections.Generic;\r\n" +"using System.Text;\r\n" +"using System;\r\n\r\n" +$"namespace {namespaceStr}\r\n" +"{\r\n" +$"\tpublic class {classNameStr} : BaseMsg\r\n" +"\t{\r\n" +$"{fieldStr}\r\n" +"\t\tpublic override int GetByteNum()\r\n" +"\t\t{\r\n" +"\t\t\tint num = 8;\r\n" + //这个8代表的是 消息ID的4个字节 + 消息体长度的4个字节$"{getByteNumStr}" +"\t\t\treturn num;\r\n" +"\t\t}\r\n\r\n" +"\t\tpublic override byte[] Writing()\r\n" +"\t\t{\r\n" +"\t\t\tint index = 0;\r\n" +"\t\t\tbyte[] bytes = new byte[GetByteNum()];\r\n" +"\t\t\tWriteInt(bytes, GetID(), ref index);\r\n" + "\t\t\tWriteInt(bytes, bytes.Length - 8, ref index);\r\n" + $"{writingStr}" +"\t\t\treturn bytes;\r\n" +"\t\t}\r\n\r\n" +"\t\tpublic override int Reading(byte[] bytes, int beginIndex = 0)\r\n" +"\t\t{\r\n" +"\t\t\tint index = beginIndex;\r\n" +$"{readingStr}" +"\t\t\treturn index - beginIndex;\r\n" +"\t\t}\r\n\r\n" +"\t\tpublic override int GetID()\r\n" +"\t\t{\r\n" +"\t\t\treturn " + idStr + ";\r\n" +"\t\t}\r\n\r\n" +"\t}\r\n" +"}";//保存为 脚本对象//保存文件的路径string path = SAVE_PATH + namespaceStr + "/Msg/";//如果没有该路径 就创建if (!Directory.Exists(path))Directory.CreateDirectory(path);//字符串保存 存储为数据结构类脚本File.WriteAllText(path + classNameStr + ".cs", dataStr);//生成处理器脚本//判断消息处理器脚本是否存在 如果存在 就不要覆盖了 避免把写过的逻辑处理代码覆盖了//如果想要改变 那就直接把没用的删了 它就会自动生成if (File.Exists(path + classNameStr + "Handler.cs"))continue;string handlerStr = $"namespace {namespaceStr}\r\n" +"{\r\n" +$"\tpublic class {classNameStr}Handler : BaseHandler\r\n" + "\t{\r\n" + "\t\tpublic override void MsgHandle()\r\n" +"\t\t{\r\n" + $"\t\t\t{classNameStr} msg = message as {classNameStr};\r\n" + "\t\t}\r\n" + "\t}\r\n" + "}\r\n";//把消息处理器类的内容保存到本地File.WriteAllText(path + classNameStr + "Handler.cs", handlerStr);}Debug.Log("消息类生成结束");}//生成消息池 主要就是ID和消息类型以及消息处理器类型的对应关系public void GenerateMsgPool(XmlNodeList nodes){List<string> ids = new List<string>();List<string> names = new List<string>();List<string> nameSpaces = new List<string>();foreach (XmlNode dataNode in nodes){//记录所有消息的IDstring id = dataNode.Attributes["id"].Value;if (!ids.Contains(id))ids.Add(id);elseDebug.LogError("存在相同ID的消息" + id);//记录所有消息的名字string name = dataNode.Attributes["name"].Value;if (!names.Contains(name))names.Add(name);elseDebug.LogError("存在相同名字的消息" + id + ",建议即使在不同命名空间中也不要要同名的信息");//获取所有消息的命名空间string msgNamespace = dataNode.Attributes["namespace"].Value;if (!nameSpaces.Contains(msgNamespace))nameSpaces.Add(msgNamespace);//获取所有消息注册相关的内容string registerStr = "";for (int i = 0; i < ids.Count; i++){registerStr += $"\t\tRegister({ids[i]}, typeof({names[i]}), typeof({names[i]}Handler));\r\n";}//获取所有需要引用的命名空间 string nameSpaceStr = "";for (int i = 0; i < nameSpaces.Count; i++){nameSpaceStr += $"using {nameSpaces[i]};\r\n";}//消息池对应的类的字符串信息string msgPoolStr = "using System;\r\n" +"using System.Collections.Generic;\r\n" +nameSpaceStr +"public class MsgPool\r\n" +"{\r\n" +"\tprivate Dictionary<int, Type> messages = new Dictionary<int, Type>();\r\n" +"\tprivate Dictionary<int, Type> handlers = new Dictionary<int, Type>();\r\n\r\n" +"\tpublic MsgPool()\r\n" +"\t{\r\n" +registerStr +"\t}\r\n" +"\tpublic void Register(int id, Type messageType, Type handlerType)\r\n" +"\t{\r\n" +"\t\tmessages.Add(id, messageType);\r\n" +"\t\thandlers.Add(id, handlerType);\r\n" +"\t}\r\n" +"\tpublic BaseMsg GetMessage(int id)\r\n" +"\t{\r\n" +"\t\tif (!messages.ContainsKey(id))\r\n" +"\t\t\treturn null;\r\n" +"\t\treturn Activator.CreateInstance(messages[id]) as BaseMsg;\r\n" +"\t}\r\n" +"\tpublic BaseHandler GetHandler(int id)\r\n" +"\t{\r\n" +"\t\tif (!handlers.ContainsKey(id))\r\n" +"\t\t\treturn null;\r\n" +"\t\treturn Activator.CreateInstance(handlers[id]) as BaseHandler;\r\n" +"\t}\r\n" +"}\r\n";//看看有没有这个路径string path = SAVE_PATH + "/Pool/";if (!Directory.Exists(path))Directory.CreateDirectory(path);//保存到本地File.WriteAllText(path + "MsgPool.cs", msgPoolStr);Debug.Log("消息池生成结束");}}/// <summary>/// 获取成员变量声明内容/// </summary>/// <param name="fields"></param>/// <returns></returns>private string GetFieldStr(XmlNodeList fields){string fieldStr = "";foreach (XmlNode field in fields){//变量类型string type = field.Attributes["type"].Value;//变量名string fieldName = field.Attributes["name"].Value;if (type == "list"){string T = field.Attributes["T"].Value;fieldStr += "\t\tpublic " + "List<" + T + "> ";}else if (type == "array"){string data = field.Attributes["data"].Value;fieldStr += "\t\tpublic " + data + "[] ";}else if(type == "dic"){string Tkey = field.Attributes["Tkey"].Value;string Tvalue = field.Attributes["Tvalue"].Value;fieldStr += "\t\tpublic " + "Dictionary<" + Tkey + ", " + Tvalue + "> ";}else if(type == "enum"){string data = field.Attributes["data"].Value;fieldStr += "\t\tpublic " + data + " ";}else{fieldStr += "\t\tpublic " + type + " ";}fieldStr += fieldName + ";\r\n";}return fieldStr;}/// <summary>/// 拼接 GetBytesNum函数的方法/// </summary>/// <param name="fields"></param>/// <returns></returns>private string GetGetBytesNumStr(XmlNodeList fields){string bytesNumStr = "";string type = "";string name = "";foreach (XmlNode field in fields){type = field.Attributes["type"].Value;name = field.Attributes["name"].Value;if (type == "list"){string T = field.Attributes["T"].Value;bytesNumStr += "\t\t\tnum += 2;\r\n"; // 2 是为了节约字节数 用一个short去存储数量信息bytesNumStr += "\t\t\tfor(int i = 0; i < " + name + ".Count; i++)\r\n";//这里使用的是 name + [i] 目的是获取 list当中的元素传入进行使用bytesNumStr += "\t\t\t\tnum += " + GetValueBytesNum(T, name + "[i]") + ";\r\n";}else if (type == "array"){string data = field.Attributes["data"].Value;bytesNumStr += "\t\t\tnum += 2;\r\n";bytesNumStr += "\t\t\tfor(int i = 0; i < " + name + ".Length; i++)\r\n";bytesNumStr += "\t\t\t\tnum += " + GetValueBytesNum(data, name + "[i]") + ";\r\n";}else if (type == "dic"){string Tkey = field.Attributes["Tkey"].Value;string Tvalue = field.Attributes["Tvalue"].Value;bytesNumStr += "\t\t\tnum += 2;\r\n";bytesNumStr += "\t\t\tforeach(" + Tkey + " key in " + name + ".Keys)\r\n";bytesNumStr += "\t\t\t{\r\n";bytesNumStr += "\t\t\t\tnum += " + GetValueBytesNum(Tkey, "key") + ";\r\n";bytesNumStr += "\t\t\t\tnum += " + GetValueBytesNum(Tvalue, name + "[key]") + ";\r\n";bytesNumStr += "\t\t\t}\r\n";}elsebytesNumStr += "\t\t\tnum += " + GetValueBytesNum(type, name) + ";\r\n";}return bytesNumStr;}//获取指定类型的字节数private string GetValueBytesNum(string type, string name){//其它类型根据需求添加switch (type){case "int":case "float":case "enum":return "4";case "bool":case "byte":return "1";case "short":return "2";case "long":case "double":return "8";case "string":return "4 + Encoding.UTF8.GetByteCount(" + name + ")";default:return name + ".GetByteNum()";}}/// <summary>/// 拼接 Writing函数的方法/// </summary>/// <param name="field"></param>/// <returns></returns>private string GetWritingStr(XmlNodeList fields){string writingStr = "";string type = "";string name = "";foreach (XmlNode field in fields){type = field.Attributes["type"].Value;name = field.Attributes["name"].Value;if (type == "list"){string T = field.Attributes["T"].Value;writingStr += "\t\t\tWriteShort(bytes, (short)" + name + ".Count, ref index);\r\n";writingStr += "\t\t\tfor(int i = 0; i < " + name + ".Count; i++)\r\n";writingStr += "\t\t\t\t" + GetFieldWritingStr(T, name + "[i]") + "\r\n";}else if(type == "array"){string data = field.Attributes["data"].Value;writingStr += "\t\t\tWriteShort(bytes, (short)" + name + ".Length, ref index);\r\n";writingStr += "\t\t\tfor(int i = 0; i < " + name + ".Length; i++)\r\n";writingStr += "\t\t\t\t" + GetFieldWritingStr(data, name + "[i]") + "\r\n";}else if (type == "dic"){string Tkey = field.Attributes["Tkey"].Value;string Tvalue = field.Attributes["Tvalue"].Value;writingStr += "\t\t\tWriteShort(bytes, (short)" + name + ".Count, ref index);\r\n";writingStr += "\t\t\tforeach(" + Tkey + " key in " + name + ".Keys)\r\n";writingStr += "\t\t\t{\r\n";writingStr += "\t\t\t\t" + GetFieldWritingStr(Tkey, "key") + "\r\n";writingStr += "\t\t\t\t" + GetFieldWritingStr(Tvalue, name + "[key]") + "\r\n";writingStr += "\t\t\t}\r\n";}else{writingStr += "\t\t\t" + GetFieldWritingStr(type, name) + "\r\n";}}return writingStr;}private string GetFieldWritingStr(string type, string name){switch (type){case "byte":return "WriteByte(bytes, " + name + ", ref index);";case "int":return "WriteInt(bytes, " + name + ", ref index);";case "short":return "WriteShort(bytes, " + name + ", ref index);";case "long":return "WriteLong(bytes, " + name + ", ref index);";case "float":return "WriteFloat(bytes, " + name + ", ref index);";case "bool":return "WriteBool(bytes, " + name + ", ref index);";case "string":return "WriteString(bytes, " + name + ", ref index);";case "enum":return "WriteInt(bytes, Convert.ToInt32(" + name + "), ref index);"; default:return "WriteData(bytes, " + name + ", ref index);";}}//private string GetReadingStr(XmlNodeList fields){string readingStr = "";string type = "";string name = "";foreach (XmlNode field in fields){type = field.Attributes["type"].Value;name = field.Attributes["name"].Value;if (type == "list"){string T = field.Attributes["T"].Value;readingStr += "\t\t\t" + name + " = new List<" + T + ">();\r\n";readingStr += "\t\t\tshort " + name + "Count = ReadShort(bytes, ref index);\r\n";readingStr += "\t\t\tfor(int i = 0; i < " + name + "Count; i++)\r\n";readingStr += "\t\t\t\t" + name + ".Add(" + GetFieldReadingStr(T) + ");\r\n";}else if (type == "array"){string data = field.Attributes["data"].Value; readingStr += "\t\t\tshort " + name + "Length = ReadShort(bytes, ref index);\r\n";readingStr += "\t\t\t" + name + " = new " + data + "[" + name + "Length];\r\n";readingStr += "\t\t\tfor(int i = 0; i < " + name + "Length; i++)\r\n";readingStr += "\t\t\t\t" + name + "[i] = " + GetFieldReadingStr(data) + ";\r\n";}else if (type == "dic"){string Tkey = field.Attributes["Tkey"].Value;string Tvalue = field.Attributes["Tvalue"].Value;readingStr += "\t\t\t" + name + " = new Dictionary<" + Tkey + ", " + Tvalue + ">();\r\n";readingStr += "\t\t\tshort " + name + "Count = ReadShort(bytes, ref index);\r\n";readingStr += "\t\t\tfor(int i = 0; i < " + name + "Count; i++)\r\n";readingStr += "\t\t\t\t" + name + ".Add(" + GetFieldReadingStr(Tkey)+ ", " + GetFieldReadingStr(Tvalue) + ");\r\n";}else if (type == "enum"){string data = field.Attributes["data"].Value;readingStr += "\t\t\t" + name + " = (" + data + ")ReadInt(bytes, ref index);\r\n"; }elsereadingStr += "\t\t\t" + name + " = " + GetFieldReadingStr(type) + ";\r\n";}return readingStr;}private string GetFieldReadingStr(string type){switch (type){case "byte":return "ReadByte(bytes, ref index)";case "int":return "ReadInt(bytes, ref index)";case "short":return "ReadShort(bytes, ref index)";case "long":return "ReadLong(bytes, ref index)";case "bool":return "ReadBool(bytes, ref index)";case "float":return "ReadFloat(bytes, ref index)";case "string":return "ReadString(bytes, ref index)";default:return "ReadData<" + type + ">(bytes, ref index)";}}}