using System; using System.Collections.Generic; using System.IO; using System.Text; namespace ET { internal class OpcodeInfo { public string Name; public int Opcode; } public static class Program { public static void Main() { // InnerMessage.proto生成cs代码 InnerProto2CS.Proto2CS(); Console.WriteLine("proto2cs succeed!"); } } public static class InnerProto2CS { private const string protoPath = "./"; private const string clientMessagePath = "../Unity/Assets/Hotfix/Logic/Model/Generate/Message/"; private const string serverMessagePath = "../Server/Model/Generate/Message/"; private static readonly char[] splitChars = { ' ', '\t', '=' }; private static readonly List msgOpcode = new List(); public static void Proto2CS() { msgOpcode.Clear(); Proto2CS("ET", "../Proto/InnerMessage.proto", serverMessagePath, "InnerOpcode", 10000); GenerateOpcode("ET","InnerOpcode", "InnerOpcode", serverMessagePath); Proto2CS("ET", "../Proto/InnerBsonMessage.proto", serverMessagePath, "InnerOpcode", 40000); GenerateOpcode("ET", "InnerBsonOpcode","InnerOpcode", serverMessagePath); Proto2CS("ET", "../Proto/OuterMessage.proto", serverMessagePath, "OuterOpcode", 20000); GenerateOpcode("ET","OuterOpcode", "OuterOpcode", serverMessagePath); Proto2CS("ET", "../Proto/OuterMessage.proto", clientMessagePath, "OuterOpcode", 20000); GenerateOpcode("ET", "OuterOpcode","OuterOpcode", clientMessagePath); } public static void Proto2CS(string ns, string protoName, string outputPath, string opcodeClassName, int startOpcode) { if (!Directory.Exists(outputPath)) { Directory.CreateDirectory(outputPath); } msgOpcode.Clear(); string proto = Path.Combine(protoPath, protoName); string csPath = Path.Combine(outputPath, Path.GetFileNameWithoutExtension(proto) + ".cs"); string s = File.ReadAllText(proto); StringBuilder sb = new StringBuilder(); sb.Append("using ET;\n"); sb.Append("using ProtoBuf;\n"); sb.Append("using System.Collections.Generic;\n"); sb.Append($"namespace {ns}\n"); sb.Append("{\n"); bool isMsgStart = false; string parentClass = ""; bool isEnumStart = false; bool isOutEnumStart = false; foreach (string line in s.Split('\n')) { string newline = line.Trim(); if (newline == "") { continue; } if (newline.StartsWith("//ResponseType")) { string responseType = line.Split(" ")[1].TrimEnd('\r', '\n'); sb.AppendLine($"\t[ResponseType(typeof({responseType}))]"); continue; } if (newline.StartsWith("//")) { sb.Append($"{newline}\n"); } //IActorLocationResponse IActorResponse IResponse string msgName = null; parentClass = ""; if (newline.StartsWith("message")) { msgName = newline.Split(splitChars, StringSplitOptions.RemoveEmptyEntries)[1]; string[] ss = newline.Split(new[] { "//" }, StringSplitOptions.RemoveEmptyEntries); if (ss.Length == 2) { parentClass = ss[1].Trim(); //if (parentClass == "IActorLocationRequest" || parentClass == "IActorRequest" || parentClass == "IRequest") //{ // string responseType = line.Split(" ")[1].TrimEnd('\r', '\n'); // Char char1 = responseType[0]; // Char char3 = responseType[2]; // responseType = char3 + "2" + char1 + responseType.Remove(0, 3); // sb.AppendLine($"\t[ResponseType(typeof({responseType}))]"); //} } isMsgStart = true; msgOpcode.Add(new OpcodeInfo() { Name = msgName, Opcode = ++startOpcode }); sb.Append($"\t[Message({opcodeClassName}.{msgName})]\n"); sb.Append($"\t[ProtoContract]\n"); sb.Append($"\tpublic partial class {msgName}"); if (parentClass == "IActorMessage" || parentClass == "IActorRequest" || parentClass == "IActorResponse") { sb.Append($":{parentClass}\n"); } else if (parentClass != "") { sb.Append($":{parentClass}\n"); } else { sb.Append("\n"); } continue; } if (isOutEnumStart&&newline == "{") { sb.Append("\t{\n"); continue; } if (isOutEnumStart&&newline == "}") { isOutEnumStart = false; sb.Append("\t}\n\n"); continue; } if (newline.StartsWith("enum")) { isOutEnumStart = true; sb.AppendLine("\tpublic " + line); continue; } if (isOutEnumStart) { sb.AppendLine(line.Replace(';', ',')); continue; } if (isMsgStart) { if (newline == "{") { sb.Append("\t{\n"); continue; } if (newline == "}") { if (isEnumStart) isEnumStart = false; else isMsgStart = false; sb.Append("\t}\n\n"); continue; } if (newline.Trim().StartsWith("//")) { sb.AppendLine(newline); continue; } if (newline.StartsWith("enum")) { isEnumStart = true; sb.AppendLine("\tpublic "+line); continue; } if (isEnumStart) { sb.AppendLine(line.Replace(';',',')); continue; } if (newline.Trim() != "" && newline != "}") { if (newline.StartsWith("repeated")) { Repeated(sb, ns, newline); } else { Members(sb, newline, true); } } } } sb.Append("}\n"); using FileStream txt = new FileStream(csPath, FileMode.Create, FileAccess.ReadWrite); using StreamWriter sw = new StreamWriter(txt); sw.Write(sb.ToString()); } private static void GenerateOpcode(string ns, string outputFileName,string outputClassName, string outputPath) { if (!Directory.Exists(outputPath)) { Directory.CreateDirectory(outputPath); } StringBuilder sb = new StringBuilder(); sb.AppendLine($"namespace {ns}"); sb.AppendLine("{"); sb.AppendLine($"\tpublic static partial class {outputClassName}"); sb.AppendLine("\t{"); foreach (OpcodeInfo info in msgOpcode) { sb.AppendLine($"\t\t public const ushort {info.Name} = {info.Opcode};"); } sb.AppendLine("\t}"); sb.AppendLine("}"); string csPath = Path.Combine(outputPath, outputFileName + ".cs"); using FileStream txt = new FileStream(csPath, FileMode.Create); using StreamWriter sw = new StreamWriter(txt); sw.Write(sb.ToString()); } private static void Repeated(StringBuilder sb, string ns, string newline) { try { int index = newline.IndexOf(";"); newline = newline.Remove(index); string[] ss = newline.Split(splitChars, StringSplitOptions.RemoveEmptyEntries); string type = ss[1]; type = ConvertType(type); string name = ss[2]; int n = int.Parse(ss[3]); sb.Append($"\t\t[ProtoMember({n})]\n"); sb.Append($"\t\tpublic List<{type}> {name} = new List<{type}>();\n\n"); } catch (Exception e) { Console.WriteLine($"{newline}\n {e}"); } } private static string ConvertType(string type) { string typeCs = ""; switch (type) { case "int16": typeCs = "short"; break; case "int32": typeCs = "int"; break; case "bytes": typeCs = "byte[]"; break; case "uint32": typeCs = "uint"; break; case "long": typeCs = "long"; break; case "int64": typeCs = "long"; break; case "uint64": typeCs = "ulong"; break; case "uint16": typeCs = "ushort"; break; default: typeCs = type; break; } return typeCs; } private static void EnumMember(StringBuilder sb, string ns, string newline) { try { int index = newline.IndexOf(";"); newline = newline.Remove(index); string[] ss = newline.Split(splitChars, StringSplitOptions.RemoveEmptyEntries); string type = ss[1]; type = ConvertType(type); string name = ss[2]; int n = int.Parse(ss[3]); sb.Append($"\t\t[ProtoMember({n})]\n"); sb.Append($"\t\tpublic List<{type}> {name} = new List<{type}>();\n\n"); } catch (Exception e) { Console.WriteLine($"{newline}\n {e}"); } } private static void Members(StringBuilder sb, string newline, bool isRequired) { try { int index = newline.IndexOf(";"); newline = newline.Remove(index); string[] ss = newline.Split(splitChars, StringSplitOptions.RemoveEmptyEntries); string type = ss[0]; string name = ss[1]; int n = int.Parse(ss[2]); string typeCs = ConvertType(type); sb.Append($"\t\t[ProtoMember({n})]\n"); sb.Append($"\t\tpublic {typeCs} {name} {{ get; set; }}\n\n"); } catch (Exception e) { Console.WriteLine($"{newline}\n {e}"); } } } }