using System; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Emit; using MongoDB.Bson.Serialization; using OfficeOpenXml; using ProtoBuf; using LicenseContext = OfficeOpenXml.LicenseContext; namespace ET { public enum ConfigType { Client, Server, } struct HeadInfo { public string FieldAttribute; public string FieldDesc; public string FieldName; public string FieldType; public HeadInfo(string cs, string desc, string name, string type) { this.FieldAttribute = cs; this.FieldDesc = desc; this.FieldName = name; this.FieldType = type; } } public static class ExcelExporter { private static string template; private const string clientClassDir = "../Unity/Codes/Model/Generate/Config"; private const string serverClassDir = "../Server/Model/Generate/Config"; private const string excelDir = "../Excel"; private const string jsonDir = "./{0}/Json"; private const string clientProtoDir = "../Unity/Assets/Bundles/Config"; private const string serverProtoDir = "../Config"; public static void Export() { try { template = File.ReadAllText("Template.txt"); ExcelPackage.LicenseContext = LicenseContext.NonCommercial; foreach (string path in Directory.GetFiles(excelDir)) { string fileName = Path.GetFileName(path); if (!fileName.EndsWith(".xlsx") || fileName.StartsWith("~$")) { continue; } using Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); using ExcelPackage p = new ExcelPackage(stream); string name = Path.GetFileNameWithoutExtension(path); ExportExcelClass(p, name, ConfigType.Client); ExportExcelClass(p, name, ConfigType.Server); ExportExcelJson(p, name, ConfigType.Client); ExportExcelJson(p, name, ConfigType.Server); } ExportExcelProtobuf(ConfigType.Client); ExportExcelProtobuf(ConfigType.Server); Console.WriteLine("导表成功!"); } catch (Exception e) { Console.WriteLine(e); } } private static string GetProtoDir(ConfigType configType) { if (configType == ConfigType.Client) { return clientProtoDir; } return serverProtoDir; } private static string GetClassDir(ConfigType configType) { if (configType == ConfigType.Client) { return clientClassDir; } return serverClassDir; } #region 导出class static void ExportExcelClass(ExcelPackage p, string name, ConfigType configType) { List classField = new List(); HashSet uniqeField = new HashSet(); foreach (ExcelWorksheet worksheet in p.Workbook.Worksheets) { ExportSheetClass(worksheet, classField, uniqeField, configType); } ExportClass(name, classField, configType); } static void ExportSheetClass(ExcelWorksheet worksheet, List classField, HashSet uniqeField, ConfigType configType) { const int row = 2; for (int col = 3; col <= worksheet.Dimension.End.Column; ++col) { string fieldName = worksheet.Cells[row + 2, col].Text.Trim(); if (fieldName == "") { continue; } if (!uniqeField.Add(fieldName)) { continue; } string fieldCS = worksheet.Cells[row, col].Text.Trim(); string fieldDesc = worksheet.Cells[row + 1, col].Text.Trim(); string fieldType = worksheet.Cells[row + 3, col].Text.Trim(); classField.Add(new HeadInfo(fieldCS, fieldDesc, fieldName, fieldType)); } } static void ExportClass(string protoName, List classField, ConfigType configType) { string dir = GetClassDir(configType); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } string exportPath = Path.Combine(dir, $"{protoName}.cs"); using FileStream txt = new FileStream(exportPath, FileMode.Create); using StreamWriter sw = new StreamWriter(txt); StringBuilder sb = new StringBuilder(); for (int i = 0; i < classField.Count; i++) { HeadInfo headInfo = classField[i]; if (headInfo.FieldAttribute.StartsWith("#")) { continue; } sb.Append($"\t\t[ProtoMember({i + 1})]\n"); sb.Append($"\t\tpublic {headInfo.FieldType} {headInfo.FieldName} {{ get; set; }}\n"); } string content = template.Replace("(ConfigName)", protoName).Replace(("(Fields)"), sb.ToString()); sw.Write(content); } #endregion #region 导出json static void ExportExcelJson(ExcelPackage p, string name, ConfigType configType) { StringBuilder sb = new StringBuilder(); sb.AppendLine("{\"list\":["); foreach (ExcelWorksheet worksheet in p.Workbook.Worksheets) { ExportSheetJson(worksheet, configType, sb); } sb.AppendLine("]}"); string dir = string.Format(jsonDir, configType.ToString()); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } string jsonPath = Path.Combine(dir, $"{name}.txt"); using FileStream txt = new FileStream(jsonPath, FileMode.Create); using StreamWriter sw = new StreamWriter(txt); sw.Write(sb.ToString()); } static void ExportSheetJson(ExcelWorksheet worksheet, ConfigType configType, StringBuilder sb) { int infoRow = 2; HeadInfo[] headInfos = new HeadInfo[100]; for (int col = 3; col <= worksheet.Dimension.End.Column; ++col) { string fieldCS = worksheet.Cells[infoRow, col].Text.Trim(); if (fieldCS.Contains("#")) { continue; } string fieldName = worksheet.Cells[infoRow + 2, col].Text.Trim(); if (fieldName == "") { continue; } string fieldDesc = worksheet.Cells[infoRow + 1, col].Text.Trim(); string fieldType = worksheet.Cells[infoRow + 3, col].Text.Trim(); headInfos[col] = new HeadInfo(fieldCS, fieldDesc, fieldName, fieldType); } for (int row = 6; row <= worksheet.Dimension.End.Row; ++row) { if (worksheet.Cells[row, 3].Text.Trim() == "") { continue; } sb.Append("{"); for (int col = 3; col <= worksheet.Dimension.End.Column; ++col) { HeadInfo headInfo = headInfos[col]; if (headInfo.FieldAttribute == null) { continue; } if (headInfo.FieldAttribute.Contains("#")) { continue; } if (headInfo.FieldName == "Id") { headInfo.FieldName = "_id"; } else { sb.Append(","); } sb.Append($"\"{headInfo.FieldName}\":{Convert(headInfo.FieldType, worksheet.Cells[row, col].Text.Trim())}"); } sb.Append("},\n"); } } private static string Convert(string type, string value) { switch (type) { case "int[]": case "int32[]": case "long[]": return $"[{value}]"; case "string[]": return $"[{value}]"; case "int": case "int32": case "int64": case "long": case "float": case "double": if (value == "") { return "0"; } return value; case "string": return $"\"{value}\""; default: throw new Exception($"不支持此类型: {type}"); } } #endregion // 根据生成的类,动态编译把json转成protobuf private static void ExportExcelProtobuf(ConfigType configType) { string classPath = GetClassDir(configType); List syntaxTrees = new List(); List protoNames = new List(); foreach (string classFile in Directory.GetFiles(classPath, "*.cs")) { protoNames.Add(Path.GetFileNameWithoutExtension(classFile)); syntaxTrees.Add(CSharpSyntaxTree.ParseText(File.ReadAllText(classFile))); } List references = new List(); Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { if (assembly.IsDynamic) { continue; } if (assembly.Location == "") { continue; } } catch (Exception e) { Console.WriteLine(e); throw; } PortableExecutableReference reference = MetadataReference.CreateFromFile(assembly.Location); references.Add(reference); } CSharpCompilation compilation = CSharpCompilation.Create( null, syntaxTrees.ToArray(), references.ToArray(), new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); using MemoryStream memSteam = new MemoryStream(); EmitResult emitResult = compilation.Emit(memSteam); if (!emitResult.Success) { StringBuilder stringBuilder = new StringBuilder(); foreach (Diagnostic t in emitResult.Diagnostics) { stringBuilder.AppendLine(t.GetMessage()); } throw new Exception($"动态编译失败:\n{stringBuilder}"); } memSteam.Seek(0, SeekOrigin.Begin); Assembly ass = Assembly.Load(memSteam.ToArray()); string dir = GetProtoDir(configType); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } foreach (string protoName in protoNames) { Type type = ass.GetType($"ET.{protoName}Category"); Type subType = ass.GetType($"ET.{protoName}"); Serializer.NonGeneric.PrepareSerializer(type); Serializer.NonGeneric.PrepareSerializer(subType); string json = File.ReadAllText(Path.Combine(string.Format(jsonDir, configType), $"{protoName}.txt")); object deserialize = BsonSerializer.Deserialize(json, type); string path = Path.Combine(dir, $"{protoName}Category.bytes"); using FileStream file = File.Create(path); Serializer.Serialize(file, deserialize); } } } }