[+] TEngineServer

[+] TEngineServer
This commit is contained in:
ALEXTANG
2023-07-13 17:17:26 +08:00
parent a69f53592e
commit 0c8f3a5f92
790 changed files with 52737 additions and 2533 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bd2dcfcb8e4f2cd458f5b287c26f8dc4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 374826e14ee51d24b9386dcdba282238
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,144 @@
#if TENGINE_NET
using System.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using ProtoBuf;
#pragma warning disable CS8600
#pragma warning disable CS8601
namespace TEngine.Core;
public static class DynamicAssembly
{
public static Assembly Load(string path)
{
var fileList = new List<string>();
// 找到所有需要加载的CS文件
foreach (string file in Directory.GetFiles(path))
{
if (Path.GetExtension(file) != ".cs")
{
continue;
}
fileList.Add(file);
}
var syntaxTreeList = new List<SyntaxTree>();
foreach (var file in fileList)
{
using var fileStream = new StreamReader(file);
var cSharp = CSharpSyntaxTree.ParseText(fileStream.ReadToEnd());
syntaxTreeList.Add(cSharp);
}
AssemblyMetadata assemblyMetadata;
MetadataReference metadataReference;
var currentDomain = AppDomain.CurrentDomain;
var assemblyName = Path.GetRandomFileName();
var assemblyArray = currentDomain.GetAssemblies();
var metadataReferenceList = new List<MetadataReference>();
// 注册引用
foreach (var domainAssembly in assemblyArray)
{
if (string.IsNullOrEmpty(domainAssembly.Location))
{
continue;
}
assemblyMetadata = AssemblyMetadata.CreateFromFile(domainAssembly.Location);
metadataReference = assemblyMetadata.GetReference();
metadataReferenceList.Add(metadataReference);
}
// 添加ProtoEntity支持
assemblyMetadata = AssemblyMetadata.CreateFromFile(typeof(AProto).Assembly.Location);
metadataReference = assemblyMetadata.GetReference();
metadataReferenceList.Add(metadataReference);
// 添加ProtoBuf.net支持
assemblyMetadata = AssemblyMetadata.CreateFromFile(typeof(ProtoMemberAttribute).Assembly.Location);
metadataReference = assemblyMetadata.GetReference();
metadataReferenceList.Add(metadataReference);
CSharpCompilation compilation = CSharpCompilation.Create(assemblyName, syntaxTreeList, metadataReferenceList,
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
using var ms = new MemoryStream();
var result = compilation.Emit(ms);
if (!result.Success)
{
foreach (var resultDiagnostic in result.Diagnostics)
{
Exporter.LogError(resultDiagnostic.GetMessage());
}
throw new Exception("failures");
}
ms.Seek(0, SeekOrigin.Begin);
return Assembly.Load(ms.ToArray());
}
public static DynamicConfigDataType GetDynamicInfo(Assembly dynamicAssembly, string tableName)
{
var dynamicConfigDataType = new DynamicConfigDataType
{
ConfigDataType = GetConfigType(dynamicAssembly, $"{tableName}Data"),
ConfigType = GetConfigType(dynamicAssembly, $"{tableName}")
};
dynamicConfigDataType.ConfigData = CreateInstance(dynamicConfigDataType.ConfigDataType);
var listPropertyType = dynamicConfigDataType.ConfigDataType.GetProperty("List");
if (listPropertyType == null)
{
throw new Exception("No Property named Add was found");
}
dynamicConfigDataType.Obj = listPropertyType.GetValue(dynamicConfigDataType.ConfigData);
dynamicConfigDataType.Method = listPropertyType.PropertyType.GetMethod("Add");
if (dynamicConfigDataType.Method == null)
{
throw new Exception("No method named Add was found");
}
return dynamicConfigDataType;
}
private static Type GetConfigType(Assembly dynamicAssembly, string typeName)
{
var configType = dynamicAssembly.GetType($"TEngine.{typeName}");
if (configType == null)
{
throw new FileNotFoundException($"TEngine.{typeName} not found");
}
return configType;
// return dynamicAssembly.GetType($"TEngine.{typeName}");
}
public static AProto CreateInstance(Type configType)
{
var config = (AProto) Activator.CreateInstance(configType);
if (config == null)
{
throw new Exception($"{configType.Name} is Activator.CreateInstance error");
}
return config;
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5ba993a045ee2d94da4827d5eff902af
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,16 @@
#if TENGINE_NET
using System.Reflection;
using System.Text;
namespace TEngine.Core;
public class DynamicConfigDataType
{
public AProto ConfigData;
public Type ConfigDataType;
public Type ConfigType;
public MethodInfo Method;
public object Obj;
public StringBuilder Json = new StringBuilder();
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 85d5d45679512064fa1c6dbbbc5ff29c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,75 @@
#if TENGINE_NET
namespace TEngine.Core;
public static class ExcelDefine
{
/// <summary>
/// 项目跟目录路径
/// </summary>
private const string ProjectPath = "../../..";
/// <summary>
/// 配置文件根目录
/// </summary>
public static string ProgramPath = $"{ProjectPath}/Config/Excel/";
/// <summary>
/// 版本文件Excel
/// </summary>
public static string ExcelVersionFile = $"{ProgramPath}Version.txt";
/// <summary>
/// 服务器代码生成文件夹
/// </summary>
public static string ServerFileDirectory = $"{ProjectPath}/Server/TEngine.Model/Generate/ConfigTable/Entity/";
/// <summary>
/// 客户端代码生成文件夹
/// </summary>
public static string ClientFileDirectory = $"{ProjectPath}/Client/Unity/Assets/Scripts/TEngine/TEngine.Model/Generate/ConfigTable/Entity/";
/// <summary>
/// 服务器二进制数据文件夹
/// </summary>
public static string ServerBinaryDirectory = $"{ProjectPath}/Config/Binary/";
/// <summary>
/// 客户端二进制数据文件夹
/// </summary>
public static string ClientBinaryDirectory = $"{ProjectPath}/Client/Unity/Assets/Bundles/Config/";
/// <summary>
/// 服务器Json数据文件夹
/// </summary>
public static string ServerJsonDirectory = $"{ProjectPath}/Config/Json/Server/";
/// <summary>
/// 客户端Json数据文件夹
/// </summary>
public static string ClientJsonDirectory = $"{ProjectPath}/Config/Json/Client/";
/// <summary>
/// 服务器自定义导出代码
/// </summary>
public static string ServerCustomExportDirectory = $"{ProjectPath}/Server/TEngine.Model/Generate/CustomExport/";
/// <summary>
/// 客户端自定义导出代码
/// </summary>
public static string ClientCustomExportDirectory = $"{ProjectPath}/Client/Unity/Assets/Scripts/TEngine/TEngine.Model/Generate/CustomExport/";
/// <summary>
/// 导表支持的类型
/// </summary>
public static readonly HashSet<string> ColTypeSet = new HashSet<string>()
{
"", "0", "bool", "byte", "short", "ushort", "int", "uint", "long", "ulong", "float", "string", "AttrConfig",
"short[]", "int[]", "long[]", "float[]", "string[]"
};
/// <summary>
/// Excel生成代码模板的位置
/// </summary>
public static string ExcelTemplatePath = $"{ProjectPath}/Config/Template/ExcelTemplate.txt";
/// <summary>
/// 代码模板
/// </summary>
public static string ExcelTemplate
{
get
{
return _template ??= File.ReadAllText(Path.Combine(Environment.CurrentDirectory, $"{ProjectPath}/Config/Template/ExcelTemplate.txt"));
}
}
private static string _template;
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0dac52ee40f938641a63d624ff19dd3f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,15 @@
#if TENGINE_NET
namespace TEngine.Core;
public sealed class ExcelTable
{
public readonly string Name;
public readonly SortedDictionary<string, List<int>> ClientColInfos = new();
public readonly SortedDictionary<string, List<int>> ServerColInfos = new();
public ExcelTable(string name)
{
Name = name;
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ae216cf2a8852c54d9b1de65bc64fafa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
#if TENGINE_NET
namespace TEngine.Core;
public class ExportInfo
{
public string Name;
public FileInfo FileInfo;
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3b19f3554768a6647b36f89e6a7c2116
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,12 @@
#if TENGINE_NET
namespace TEngine.Core;
public enum ExportType
{
None = 0,
ProtoBuf = 1, // 导出ProtoBuf
AllExcelIncrement = 2, // 所有-增量导出Excel
AllExcel = 3, // 所有-全量导出Excel
Max, // 这个一定放最后
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8c7d1a9efd4b9a94bb7c4d99a7160068
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,84 @@
#if TENGINE_UNITY
using System;
using System.Collections.Generic;
using TEngine.Core;
using UnityEngine;
namespace TEngine.Core
{
public static class ConfigTableManage
{
private static readonly string ConfigBundle = "Config".ToLower();
private static readonly Dictionary<string, AProto> ConfigDic = new ();
static ConfigTableManage()
{
}
public static T Load<T>() where T : AProto
{
var dataConfig = typeof(T).Name;
if (ConfigDic.TryGetValue(dataConfig, out var aProto))
{
return (T)aProto;
}
try
{
var bytes = GameModule.Resource.LoadAsset<TextAsset>($"{ConfigBundle}-{dataConfig}").bytes;
var data = (AProto) ProtoBufHelper.FromBytes(typeof(T), bytes, 0, bytes.Length);
data.AfterDeserialization();
ConfigDic[dataConfig] = data;
return (T)data;
}
catch (Exception ex)
{
throw new Exception($"ConfigTableManage:{typeof(T).Name} 数据表加载之后反序列化时出错:{ex}");
}
}
private static AProto Load(string dataConfig, int assemblyName)
{
if (ConfigDic.TryGetValue(dataConfig, out var aProto))
{
return aProto;
}
var fullName = $"TEngine.{dataConfig}";
var assembly = AssemblyManager.GetAssembly(assemblyName);
var type = assembly.GetType(fullName);
if (type == null)
{
Log.Error($"not find {fullName} in assembly");
return null;
}
try
{
var bytes = GameModule.Resource.LoadAsset<TextAsset>($"{ConfigBundle}-{dataConfig}").bytes;
var data = (AProto) ProtoBufHelper.FromBytes(type, bytes, 0, bytes.Length);
data.AfterDeserialization();
ConfigDic[dataConfig] = data;
return data;
}
catch (Exception ex)
{
throw new Exception($"ConfigTableManage:{type.Name} 数据表加载之后反序列化时出错:{ex}");
}
}
private static void Reload()
{
foreach (var (_, aProto) in ConfigDic)
{
((IDisposable) aProto).Dispose();
}
ConfigDic.Clear();
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 30f0a5e8602ad3d4f83d9e275d1edd15
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,788 @@
#if TENGINE_NET
using System.Collections.Concurrent;
using System.Reflection;
using System.Runtime.Loader;
using System.Text;
using System.Text.RegularExpressions;
using TEngine.DataStructure;
using TEngine.Core;
using Newtonsoft.Json;
using OfficeOpenXml;
using static System.String;
#pragma warning disable CS8625
#pragma warning disable CS8604
#pragma warning disable CS8602
#pragma warning disable CS8601
#pragma warning disable CS8600
#pragma warning disable CS8618
namespace TEngine.Core;
using TableDictionary = SortedDictionary<string, List<int>>;
public sealed class ExcelExporter
{
private Dictionary<string, long> _versionDic;
private readonly Regex _regexName = new Regex("^[a-zA-Z][a-zA-Z0-9_]*$");
private readonly HashSet<string> _loadFiles = new HashSet<string> {".xlsx", ".xlsm", ".csv"};
private readonly OneToManyList<string, ExportInfo> _tables = new OneToManyList<string, ExportInfo>();
private readonly ConcurrentDictionary<string, ExcelTable> _excelTables = new ConcurrentDictionary<string, ExcelTable>();
private readonly ConcurrentDictionary<string, ExcelWorksheet> _worksheets = new ConcurrentDictionary<string, ExcelWorksheet>();
public ExcelExporter(ExportType exportType)
{
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
var versionFilePath = ExcelDefine.ExcelVersionFile;
switch (exportType)
{
case ExportType.AllExcelIncrement:
{
break;
}
case ExportType.AllExcel:
{
if (File.Exists(versionFilePath))
{
File.Delete(versionFilePath);
}
FileHelper.ClearDirectoryFile(ExcelDefine.ServerFileDirectory);
FileHelper.ClearDirectoryFile(ExcelDefine.ClientFileDirectory);
break;
}
}
Find();
Parsing();
ExportToBinary();
File.WriteAllText(versionFilePath, JsonConvert.SerializeObject(_versionDic));
CustomExport();
}
private static void CustomExport()
{
// 清除文件夹
FileHelper.ClearDirectoryFile(ExcelDefine.ServerCustomExportDirectory);
FileHelper.ClearDirectoryFile(ExcelDefine.ClientCustomExportDirectory);
// 找到程序集
var assemblyLoadContext = new AssemblyLoadContext("ExporterDll", true);
var dllBytes = File.ReadAllBytes(Path.Combine(Environment.CurrentDirectory, "TEngine.Model.dll"));
var pdbBytes = File.ReadAllBytes(Path.Combine(Environment.CurrentDirectory, "TEngine.Model.pdb"));
var assembly = assemblyLoadContext.LoadFromStream(new MemoryStream(dllBytes), new MemoryStream(pdbBytes));
// 加载程序集
AssemblyManager.LoadAssembly(int.MaxValue, assembly);
// 执行自定义导出
var task = new List<Task>();
foreach (var type in AssemblyManager.ForEach(typeof(ICustomExport)))
{
var customExport = (ICustomExport)Activator.CreateInstance(type);
if (customExport != null)
{
task.Add(Task.Run(customExport.Run));
}
}
Task.WaitAll(task.ToArray());
}
/// <summary>
/// 查找配置文件
/// </summary>
private void Find()
{
var versionFilePath = ExcelDefine.ExcelVersionFile;
if(File.Exists(versionFilePath))
{
var versionJson = File.ReadAllText(versionFilePath);
_versionDic = JsonConvert.DeserializeObject<Dictionary<string, long>>(versionJson);
}
else
{
_versionDic = new Dictionary<string, long>();
}
var dir = new DirectoryInfo(ExcelDefine.ProgramPath);
var excelFiles = dir.GetFiles("*", SearchOption.AllDirectories);
if (excelFiles.Length <= 0)
{
return;
}
foreach (var excelFile in excelFiles)
{
// 过滤掉非指定后缀的文件
if (!_loadFiles.Contains(excelFile.Extension))
{
continue;
}
var lastIndexOf = excelFile.Name.LastIndexOf(".", StringComparison.Ordinal);
if (lastIndexOf < 0)
{
continue;
}
var fullName = excelFile.FullName;
var excelName = excelFile.Name.Substring(0, lastIndexOf);
var path = fullName.Substring(0, fullName.Length - excelFile.Name.Length);
// 过滤#和~开头文件和文件夹带有#的所有文件
if (excelName.StartsWith("#", StringComparison.Ordinal) ||
excelName.StartsWith("~", StringComparison.Ordinal) ||
path.Contains("#", StringComparison.Ordinal))
{
continue;
}
if (!_regexName.IsMatch(excelName))
{
Exporter.LogError($"{excelName} 配置文件名非法");
continue;
}
_tables.Add(excelName.Split('_')[0], new ExportInfo()
{
Name = excelName, FileInfo = excelFile
});
}
var removeTables = new List<string>();
foreach (var (tableName, tableList) in _tables)
{
var isNeedExport = false;
foreach (var exportInfo in tableList)
{
var timer = TimeHelper.Transition(exportInfo.FileInfo.LastWriteTime);
if (!isNeedExport)
{
if (_versionDic.TryGetValue(exportInfo.Name, out var lastWriteTime))
{
isNeedExport = lastWriteTime != timer;
}
else
{
isNeedExport = true;
}
}
_versionDic[exportInfo.Name] = timer;
}
if (!isNeedExport)
{
removeTables.Add(tableName);
}
}
foreach (var removeTable in removeTables)
{
_tables.Remove(removeTable);
}
foreach (var (_, exportInfo) in _tables)
{
exportInfo.Sort((x, y) => Compare(x.Name, y.Name, StringComparison.Ordinal));
}
}
/// <summary>
/// 生成配置文件
/// </summary>
private void Parsing()
{
var generateTasks = new List<Task>();
foreach (var (tableName, tableList) in _tables)
{
var task = Task.Run(() =>
{
var writeToClassTask = new List<Task>();
var excelTable = new ExcelTable(tableName);
// 筛选需要导出的列
foreach (var exportInfo in tableList)
{
try
{
var serverColInfoList = new List<int>();
var clientColInfoList = new List<int>();
var worksheet = LoadExcel(exportInfo.FileInfo.FullName, true);
for (var col = 3; col <= worksheet.Columns.EndColumn; col++)
{
// 列名字第一个字符是#不参与导出
var colName = GetCellValue(worksheet, 5, col);
if (colName.StartsWith("#", StringComparison.Ordinal))
{
continue;
}
// 数值列不参与导出
var numericalCol = GetCellValue(worksheet, 3, col);
if (numericalCol != "" && numericalCol != "0")
{
continue;
}
var serverType = GetCellValue(worksheet, 1, col);
var clientType = GetCellValue(worksheet, 2, col);
var isExportServer = !IsNullOrEmpty(serverType) && serverType != "0";
var isExportClient = !IsNullOrEmpty(clientType) && clientType != "0";
if (!isExportServer && !isExportClient)
{
continue;
}
if (isExportServer && isExportClient & serverType != clientType)
{
Exporter.LogError($"配置表 {exportInfo.Name} {col} 列 [{colName}] 客户端类型 {clientType} 和 服务端类型 {serverType} 不一致");
continue;
}
if (!ExcelDefine.ColTypeSet.Contains(serverType) ||
!ExcelDefine.ColTypeSet.Contains(clientType))
{
Exporter.LogError($"配置表 {exportInfo.Name} {col} 列 [{colName}] 客户端类型 {clientType}, 服务端类型 {serverType} 不合法");
continue;
}
if (!_regexName.IsMatch(colName))
{
Exporter.LogError($"配置表 {exportInfo.Name} {col} 列 [{colName}] 列名非法");
continue;
}
serverColInfoList.Add(col);
if (isExportClient)
{
clientColInfoList.Add(col);
}
}
if (clientColInfoList.Count > 0)
{
excelTable.ClientColInfos.Add(exportInfo.FileInfo.FullName, clientColInfoList);
}
if (serverColInfoList.Count > 0)
{
excelTable.ServerColInfos.Add(exportInfo.FileInfo.FullName, serverColInfoList);
}
}
catch (Exception e)
{
Exporter.LogError($"Config : {tableName}, Name : {exportInfo.Name}, Error : {e}");
}
}
// 生成cs文件
writeToClassTask.Add(Task.Run(() =>
{
WriteToClass(excelTable.ServerColInfos, ExcelDefine.ServerFileDirectory, true);
}));
writeToClassTask.Add(Task.Run(() =>
{
WriteToClass(excelTable.ClientColInfos, ExcelDefine.ClientFileDirectory, false);
}));
Task.WaitAll(writeToClassTask.ToArray());
_excelTables.TryAdd(tableName, excelTable);
});
generateTasks.Add(task);
}
Task.WaitAll(generateTasks.ToArray());
}
/// <summary>
/// 写入到cs
/// </summary>
/// <param name="colInfos"></param>
/// <param name="exportPath"></param>
/// <param name="isServer"></param>
private void WriteToClass(TableDictionary colInfos, string exportPath, bool isServer)
{
if (colInfos.Count <= 0)
{
return;
}
var index = 0;
var fileBuilder = new StringBuilder();
var colNameSet = new HashSet<string>();
if (colInfos.Count == 0)
{
return;
}
var csName = Path.GetFileNameWithoutExtension(colInfos.First().Key)?.Split('_')[0];
foreach (var (tableName, cols) in colInfos)
{
if (cols == null || cols.Count == 0)
{
continue;
}
var excelWorksheet = LoadExcel(tableName, false);
foreach (var colIndex in cols)
{
var colName = GetCellValue(excelWorksheet, 5, colIndex);
if (colNameSet.Contains(colName))
{
continue;
}
colNameSet.Add(colName);
string colType;
if (isServer)
{
colType = GetCellValue(excelWorksheet, 1, colIndex);
if (IsNullOrEmpty(colType) || colType == "0")
{
colType = GetCellValue(excelWorksheet, 2, colIndex);
}
}
else
{
colType = GetCellValue(excelWorksheet, 2, colIndex);
}
var remarks = GetCellValue(excelWorksheet, 4, colIndex);
fileBuilder.Append($"\n\t\t[ProtoMember({++index}, IsRequired = true)]\n");
fileBuilder.Append(
IsArray(colType,out var t)
? $"\t\tpublic {colType} {colName} {{ get; set; }} = Array.Empty<{t}>(); // {remarks}"
: $"\t\tpublic {colType} {colName} {{ get; set; }} // {remarks}");
}
}
var template = ExcelDefine.ExcelTemplate;
if (fileBuilder.Length > 0)
{
if (!Directory.Exists(exportPath))
{
Directory.CreateDirectory(exportPath);
}
var content = template.Replace("(namespace)", "TEngine")
.Replace("(ConfigName)", csName)
.Replace("(Fields)", fileBuilder.ToString());
File.WriteAllText(Path.Combine(exportPath, $"{csName}.cs"), content);
}
}
/// <summary>
/// 把数据和实体类转换二进制导出到文件中
/// </summary>
private void ExportToBinary()
{
var exportToBinaryTasks = new List<Task>();
var dynamicServerAssembly = DynamicAssembly.Load(ExcelDefine.ServerFileDirectory);
var dynamicClientAssembly = DynamicAssembly.Load(ExcelDefine.ClientFileDirectory);
foreach (var (tableName, tableList) in _tables)
{
var task = Task.Run(() =>
{
var idCheck = new HashSet<string>();
var excelTable = _excelTables[tableName];
var csName = Path.GetFileNameWithoutExtension(tableName);
var serverColInfoCount = excelTable.ServerColInfos.Sum(d=>d.Value.Count);
var serverDynamicInfo = serverColInfoCount == 0 ? null : DynamicAssembly.GetDynamicInfo(dynamicServerAssembly, csName);
var clientColInfoCount = excelTable.ClientColInfos.Sum(d=>d.Value.Count);
var clientDynamicInfo = clientColInfoCount == 0 ? null : DynamicAssembly.GetDynamicInfo(dynamicClientAssembly, csName);
for (var i = 0; i < tableList.Count; i++)
{
var tableListName = tableList[i];
try
{
var fileInfoFullName = tableListName.FileInfo.FullName;
var excelWorksheet = LoadExcel(fileInfoFullName, false);
var rows = excelWorksheet.Dimension.Rows;
excelTable.ServerColInfos.TryGetValue(fileInfoFullName, out var serverCols);
excelTable.ClientColInfos.TryGetValue(fileInfoFullName, out var clientCols);
for (var row = 7; row <= rows; row++)
{
if (GetCellValue(excelWorksheet, row, 1).StartsWith("#", StringComparison.Ordinal))
{
continue;
}
var id = GetCellValue(excelWorksheet, row, 3);
if (idCheck.Contains(id))
{
Exporter.LogError($"{tableListName.Name} 存在重复Id {id} 行号 {row}");
continue;
}
idCheck.Add(id);
var isLast = row == rows && (i == tableList.Count - 1);
GenerateBinary(fileInfoFullName, excelWorksheet, serverDynamicInfo, serverCols, id, row, isLast, true);
GenerateBinary(fileInfoFullName, excelWorksheet, clientDynamicInfo, clientCols, id, row, isLast, false);
}
}
catch (Exception e)
{
Exporter.LogError($"Table:{tableListName} error! \n{e}");
throw;
}
}
if (serverDynamicInfo?.ConfigData != null)
{
var bytes = ProtoBufHelper.ToBytes(serverDynamicInfo.ConfigData);
var serverBinaryDirectory = ExcelDefine.ServerBinaryDirectory;
if (!Directory.Exists(serverBinaryDirectory))
{
Directory.CreateDirectory(serverBinaryDirectory);
}
File.WriteAllBytes(Path.Combine(serverBinaryDirectory, $"{csName}Data.bytes"), bytes);
if (serverDynamicInfo.Json.Length > 0)
{
var serverJsonDirectory = ExcelDefine.ServerJsonDirectory;
using var sw = new StreamWriter(Path.Combine(serverJsonDirectory, $"{csName}Data.Json"));
sw.WriteLine("{\"List\":[");
sw.Write(serverDynamicInfo.Json.ToString());
sw.WriteLine("]}");
}
}
if (clientDynamicInfo?.ConfigData != null)
{
var bytes = ProtoBufHelper.ToBytes(clientDynamicInfo.ConfigData);
var clientBinaryDirectory = ExcelDefine.ClientBinaryDirectory;
if (!Directory.Exists(clientBinaryDirectory))
{
Directory.CreateDirectory(clientBinaryDirectory);
}
File.WriteAllBytes(Path.Combine(clientBinaryDirectory, $"{csName}Data.bytes"), bytes);
if (clientDynamicInfo.Json.Length > 0)
{
var clientJsonDirectory = ExcelDefine.ClientJsonDirectory;
using var sw = new StreamWriter(Path.Combine(clientJsonDirectory, $"{csName}Data.Json"));
sw.WriteLine("{\"List\":[");
sw.Write(clientDynamicInfo.Json.ToString());
sw.WriteLine("]}");
}
}
});
exportToBinaryTasks.Add(task);
}
Task.WaitAll(exportToBinaryTasks.ToArray());
}
private void GenerateBinary(string fileInfoFullName, ExcelWorksheet excelWorksheet, DynamicConfigDataType dynamicInfo, List<int> cols, string id, int row, bool isLast, bool isServer)
{
if (cols == null || IsNullOrEmpty(id) || cols.Count <= 0 || dynamicInfo?.ConfigType == null)
{
return;
}
var config = DynamicAssembly.CreateInstance(dynamicInfo.ConfigType);
for (var i = 0; i < cols.Count; i++)
{
string colType;
var colIndex = cols[i];
var colName = GetCellValue(excelWorksheet, 5, colIndex);
var value = GetCellValue(excelWorksheet, row, colIndex);
if (isServer)
{
colType = GetCellValue(excelWorksheet, 1, colIndex);
if (IsNullOrEmpty(colType) || colType == "0")
{
colType = GetCellValue(excelWorksheet, 2, colIndex);
}
}
else
{
colType = GetCellValue(excelWorksheet, 2, colIndex);
}
try
{
SetNewValue(dynamicInfo.ConfigType.GetProperty(colName), config, colType, value);
}
catch (Exception e)
{
Exporter.LogError($"Error Table {fileInfoFullName} Col:{colName} colType:{colType} Row:{row} value:{value} {e}");
throw;
}
}
dynamicInfo.Method.Invoke(dynamicInfo.Obj, new object[] {config});
var json = JsonConvert.SerializeObject(config);
if (isLast)
{
dynamicInfo.Json.AppendLine(json);
}
else
{
dynamicInfo.Json.AppendLine($"{json},");
}
}
public ExcelWorksheet LoadExcel(string name, bool isAddToDic)
{
if (_worksheets.TryGetValue(name, out var worksheet))
{
return worksheet;
}
worksheet = new ExcelPackage(name).Workbook.Worksheets[0];
if (isAddToDic)
{
_worksheets.TryAdd(name, worksheet);
}
Exporter.LogInfo(name);
return worksheet;
}
private string GetCellValue(ExcelWorksheet sheet, int row, int column)
{
var cell = sheet.Cells[row, column];
try
{
if (cell.Value == null)
{
return "";
}
var s = cell.GetValue<string>();
return s.Trim();
}
catch (Exception e)
{
throw new Exception($"Rows {row} Columns {column} Content {cell.Text} {e}");
}
}
private void SetNewValue(PropertyInfo propertyInfo, AProto config, string type, string value)
{
if (IsNullOrWhiteSpace(value))
{
return;
}
switch (type)
{
case "short":
{
propertyInfo.SetValue(config, Convert.ToInt16(value));
return;
}
case "ushort":
{
propertyInfo.SetValue(config, Convert.ToUInt16(value));
return;
}
case "uint":
{
propertyInfo.SetValue(config, Convert.ToUInt32(value));
return;
}
case "int":
{
propertyInfo.SetValue(config, Convert.ToInt32(value));
return;
}
case "decimal":
{
propertyInfo.SetValue(config, Convert.ToDecimal(value));
return;
}
case "string":
{
try
{
propertyInfo.SetValue(config, value);
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
return;
}
case "bool":
{
// 空字符串
value = value.ToLower();
if (IsNullOrEmpty(value))
{
propertyInfo.SetValue(config, false);
}
else if (bool.TryParse(value, out bool b))
{
propertyInfo.SetValue(config, b);
}
else if (int.TryParse(value, out int v))
{
propertyInfo.SetValue(config, v != 0);
}
else
{
propertyInfo.SetValue(config, false);
}
return;
}
case "ulong":
{
propertyInfo.SetValue(config, Convert.ToUInt64(value));
return;
}
case "long":
{
propertyInfo.SetValue(config, Convert.ToInt64(value));
return;
}
case "double":
{
propertyInfo.SetValue(config, Convert.ToDouble(value));
return;
}
case "float":
{
propertyInfo.SetValue(config, Convert.ToSingle(value));
return;
}
case "int32[]":
case "int[]":
{
if (value != "0")
{
propertyInfo.SetValue(config, value.Split(",").Select(d => Convert.ToInt32(d)).ToArray());
}
return;
}
case "long[]":
{
if (value != "0")
{
propertyInfo.SetValue(config, value.Split(",").Select(d => Convert.ToInt64(d)).ToArray());
}
return;
}
case "double[]":
{
if (value != "0")
{
propertyInfo.SetValue(config, value.Split(",").Select(d => Convert.ToDouble(d)).ToArray());
}
return;
}
case "string[]":
{
if (value == "0")
{
return;
}
var list = value.Split(",").ToArray();
for (var i = 0; i < list.Length; i++)
{
list[i] = list[i].Replace("\"", "");
}
propertyInfo.SetValue(config, value.Split(",").ToArray());
return;
}
case "float[]":
{
if (value != "0")
{
propertyInfo.SetValue(config, value.Split(",").Select(d => Convert.ToSingle(d)).ToArray());
}
return;
}
// case "AttrConfig":
// {
// if (value.Trim() == "" || value.Trim() == "{}")
// {
// propertyInfo.SetValue(config, null);
// return;
// }
//
// var attr = new AttrConfig {KV = JsonConvert.DeserializeObject<Dictionary<int, int>>(value)};
//
// propertyInfo.SetValue(config, attr);
//
// return;
// }
default:
throw new NotSupportedException($"不支持此类型: {type}");
}
}
private bool IsArray(string type, out string t)
{
t = null;
var index = type.IndexOf("[]", StringComparison.Ordinal);
if (index >= 0)
{
t = type.Remove(index, 2);
}
return index >= 0;
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 11f644344212d644dbfd6cae6ff1f133
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,102 @@
#if TENGINE_NET
using TEngine.Core.DataBase;
using TEngine.Core;
#pragma warning disable CS8603
#pragma warning disable CS8618
namespace TEngine.Core
{
public static class ConfigTableManage
{
public static Func<uint, ServerConfigInfo> ServerConfig;
public static Func<uint, MachineConfigInfo> MachineConfig;
public static Func<uint, SceneConfigInfo> SceneConfig;
public static Func<uint, WorldConfigInfo> WorldConfigInfo;
public static Func<List<ServerConfigInfo>> AllServerConfig;
public static Func<List<MachineConfigInfo>> AllMachineConfig;
public static Func<List<SceneConfigInfo>> AllSceneConfig;
private const string BinaryDirectory = "../../../Config/Binary/";
private static readonly Dictionary<string, AProto> ConfigDic = new ();
public static T Load<T>() where T : AProto
{
var dataConfig = typeof(T).Name;
if (ConfigDic.TryGetValue(dataConfig, out var aProto))
{
return (T)aProto;
}
try
{
var configFile = GetConfigPath(dataConfig);
var bytes = File.ReadAllBytes(configFile);
var data = (AProto) ProtoBufHelper.FromBytes(typeof(T), bytes, 0, bytes.Length);
data.AfterDeserialization();
ConfigDic[dataConfig] = data;
return (T)data;
}
catch (Exception ex)
{
throw new Exception($"ConfigTableManage:{typeof(T).Name} 数据表加载之后反序列化时出错:{ex}");
}
}
private static AProto Load(string dataConfig, int assemblyName)
{
if (ConfigDic.TryGetValue(dataConfig, out var aProto))
{
return aProto;
}
var fullName = $"TEngine.{dataConfig}";
var assembly = AssemblyManager.GetAssembly(assemblyName);
var type = assembly.GetType(fullName);
if (type == null)
{
Log.Error($"not find {fullName} in assembly");
return null;
}
try
{
var configFile = GetConfigPath(type.Name);
var bytes = File.ReadAllBytes(configFile);
var data = (AProto) ProtoBufHelper.FromBytes(type, bytes, 0, bytes.Length);
data.AfterDeserialization();
ConfigDic[dataConfig] = data;
return data;
}
catch (Exception ex)
{
throw new Exception($"ConfigTableManage:{type.Name} 数据表加载之后反序列化时出错:{ex}");
}
}
private static string GetConfigPath(string name)
{
var configFile = Path.Combine(BinaryDirectory, $"{name}.bytes");
if (File.Exists(configFile))
{
return configFile;
}
throw new FileNotFoundException($"{name}.byte not found: {configFile}");
}
private static void Reload()
{
foreach (var (_, aProto) in ConfigDic)
{
((IDisposable) aProto).Dispose();
}
ConfigDic.Clear();
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 04d66af120535af4a9dbbbf4e73931a1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,130 @@
#if TENGINE_NET
using System.Diagnostics;
using System.Reflection;
using System.Runtime.Loader;
using System.Text;
using TEngine.Core;
using Microsoft.Extensions.Configuration;
#pragma warning disable CS8601
#pragma warning disable CS8618
namespace TEngine.Core;
public sealed class Exporter
{
public void Start()
{
Console.OutputEncoding = Encoding.UTF8;
var exportType = Define.Options.ExportType;
if (exportType != ExportType.None)
{
return;
}
LogInfo("请输入你想要做的操作:");
LogInfo("1:导出网络协议ProtoBuf");
LogInfo("2:增量导出Excel包含常量枚举");
LogInfo("3:全量导出Excel包含常量枚举");
var keyChar = Console.ReadKey().KeyChar;
if (!int.TryParse(keyChar.ToString(), out var key) || key is < 1 or >= (int) ExportType.Max)
{
Console.WriteLine("");
LogInfo("无法识别的导出类型请,输入正确的操作类型。");
return;
}
LogInfo("");
exportType = (ExportType) key;
LoadConfig();
switch (exportType)
{
case ExportType.ProtoBuf:
{
_ = new ProtoBufExporter();
break;
}
case ExportType.AllExcel:
case ExportType.AllExcelIncrement:
{
_ = new ExcelExporter(exportType);
break;
}
}
LogInfo("操作完成,按任意键关闭程序");
Console.ReadKey();
Environment.Exit(0);
}
private void LoadConfig()
{
const string settingsName = "TEngineSettings.json";
var currentDirectory = Directory.GetCurrentDirectory();
if (!File.Exists(Path.Combine(currentDirectory, settingsName)))
{
throw new FileNotFoundException($"not found {settingsName} in OutputDirectory");
}
var configurationRoot = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile(settingsName)
.Build();
// ProtoBuf文件所在的位置文件夹位置
ProtoBufDefine.ProtoBufDirectory = configurationRoot["Export:ProtoBufDirectory:Value"];
// ProtoBuf生成到服务端的文件夹位置
ProtoBufDefine.ServerDirectory = configurationRoot["Export:ProtoBufServerDirectory:Value"];
// ProtoBuf生成到客户端的文件夹位置
ProtoBufDefine.ClientDirectory = configurationRoot["Export:ProtoBufClientDirectory:Value"];
// ProtoBuf生成代码模板的位置
ProtoBufDefine.ProtoBufTemplatePath = configurationRoot["Export:ProtoBufTemplatePath:Value"];
// Excel配置文件根目录
ExcelDefine.ProgramPath = configurationRoot["Export:ExcelProgramPath:Value"];
// Excel版本文件的位置
ExcelDefine.ExcelVersionFile = configurationRoot["Export:ExcelVersionFile:Value"];
// Excel生成服务器代码的文件夹位置
ExcelDefine.ServerFileDirectory = configurationRoot["Export:ExcelServerFileDirectory:Value"];
// Excel生成客户端代码文件夹位置
ExcelDefine.ClientFileDirectory = configurationRoot["Export:ExcelClientFileDirectory:Value"];
// Excel生成服务器二进制数据文件夹位置
ExcelDefine.ServerBinaryDirectory = configurationRoot["Export:ExcelServerBinaryDirectory:Value"];
// Excel生成客户端二进制数据文件夹位置
ExcelDefine.ClientBinaryDirectory = configurationRoot["Export:ExcelClientBinaryDirectory:Value"];
// Excel生成服务器Json数据文件夹位置
ExcelDefine.ServerJsonDirectory = configurationRoot["Export:ExcelServerJsonDirectory:Value"];
// Excel生成客户端Json数据文件夹位置
ExcelDefine.ClientJsonDirectory = configurationRoot["Export:ExcelClientJsonDirectory:Value"];
// Excel生成代码模板的位置
ExcelDefine.ExcelTemplatePath = configurationRoot["Export:ExcelTemplatePath:Value"];
// 服务器自定义导出代码文件夹位置
ExcelDefine.ServerCustomExportDirectory = configurationRoot["Export:ServerCustomExportDirectory:Value"];
// 客户端自定义导出代码文件夹位置
ExcelDefine.ClientCustomExportDirectory = configurationRoot["Export:ClientCustomExportDirectory:Value"];
}
public static void LogInfo(string msg)
{
Console.WriteLine(msg);
}
public static void LogError(string msg)
{
ConsoleColor color = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"{msg}\n{new StackTrace(1, true)}");
Console.ForegroundColor = color;
}
public static void LogError(Exception e)
{
ConsoleColor color = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(e.Data.Contains("StackTrace") ? $"{e.Data["StackTrace"]}\n{e}" : e.ToString());
Console.ForegroundColor = color;
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2b2afd6fb4280f446aff438db7861e64
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3fa83b6f387771f48be26dc88c05e808
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,10 @@
namespace TEngine.Core
{
/// <summary>
/// 表示是一个配置文件
/// </summary>
public interface IConfigTable
{
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f0c846012d7e36e4fa17c615f15da685
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,44 @@
#if TENGINE_NET
namespace TEngine.Core;
public interface ICustomExport
{
void Run();
}
public abstract class ACustomExport : ICustomExport
{
protected enum CustomExportType
{
Client,Server
}
public abstract void Run();
protected void Write(string fileName, string fileContent, CustomExportType customExportType)
{
switch (customExportType)
{
case CustomExportType.Client:
{
if (!Directory.Exists(ExcelDefine.ClientCustomExportDirectory))
{
Directory.CreateDirectory(ExcelDefine.ClientCustomExportDirectory);
}
File.WriteAllText($"{ExcelDefine.ClientCustomExportDirectory}/{fileName}", fileContent);
return;
}
case CustomExportType.Server:
{
if (!Directory.Exists(ExcelDefine.ServerCustomExportDirectory))
{
Directory.CreateDirectory(ExcelDefine.ServerCustomExportDirectory);
}
File.WriteAllText($"{ExcelDefine.ServerCustomExportDirectory}/{fileName}", fileContent);
return;
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d55723aa5c671d544a461eb8fb524769
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5c6c545584f5eb24892e7de1da9daffd
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,28 @@
#if TENGINE_NET
namespace TEngine.Core;
public static class ProtoBufDefine
{
public static readonly char[] SplitChars = {' ', '\t'};
/// <summary>
/// 项目跟目录路径
/// </summary>
private const string ProjectPath = "../../..";
/// <summary>
/// ProtoBuf文件夹
/// </summary>
public static string ProtoBufDirectory = $"{ProjectPath}/Config/ProtoBuf/";
/// <summary>
/// 服务端生成文件夹
/// </summary>
public static string ServerDirectory = $"{ProjectPath}/Server/TEngine.Model/Generate/NetworkProtocol/";
/// <summary>
/// 客户端生成文件夹
/// </summary>
public static string ClientDirectory = $"{ProjectPath}/Client/Unity/Assets/Scripts/TEngine/TEngine.Model/Generate/NetworkProtocol/";
/// <summary>
/// 代码模板路径
/// </summary>
public static string ProtoBufTemplatePath = $"{ProjectPath}/Config/Template/ProtoTemplate.txt";
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d418e7f4b6646504c8a6ca4b381992b0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,505 @@
#if TENGINE_NET
using System.Text;
using TEngine.Core.Network;
#pragma warning disable CS8604
#pragma warning disable CS8602
#pragma warning disable CS8600
#pragma warning disable CS8618
namespace TEngine.Core;
public enum ProtoBufOpCodeType
{
None = 0,
Outer = 1,
Inner = 2,
InnerBson = 3,
}
public sealed class OpcodeInfo
{
public uint Code;
public string Name;
}
public sealed class ProtoBufExporter
{
private uint _aMessage;
private uint _aRequest;
private uint _aResponse;
private uint _aRouteMessage;
private uint _aRouteRequest;
private uint _aRouteResponse;
private string _serverTemplate;
private string _clientTemplate;
private readonly List<OpcodeInfo> _opcodes = new();
public ProtoBufExporter()
{
Console.OutputEncoding = Encoding.UTF8;
if (!Directory.Exists(ProtoBufDefine.ServerDirectory))
{
Directory.CreateDirectory(ProtoBufDefine.ServerDirectory);
}
if (!Directory.Exists(ProtoBufDefine.ClientDirectory))
{
Directory.CreateDirectory(ProtoBufDefine.ClientDirectory);
}
var tasks = new Task[2];
tasks[0] = Task.Run(RouteType);
tasks[1] = Task.Run(async () =>
{
LoadTemplate();
await Start(ProtoBufOpCodeType.Outer);
await Start(ProtoBufOpCodeType.Inner);
await Start(ProtoBufOpCodeType.InnerBson);
});
Task.WaitAll(tasks);
}
private async Task Start(ProtoBufOpCodeType opCodeType)
{
var protoFile = "";
var opCodeName = "";
var parameter = "";
var className = "";
var isMsgHead = false;
OpcodeInfo opcodeInfo = null;
string responseTypeStr = null;
string customRouteType = null;
_opcodes.Clear();
var file = new StringBuilder();
var saveDirectory = new Dictionary<string,string>();
switch (opCodeType)
{
case ProtoBufOpCodeType.Outer:
{
_aMessage = Opcode.OuterMessage;
_aRequest = Opcode.OuterRequest;
_aResponse = Opcode.OuterResponse;
_aRouteMessage = Opcode.OuterRouteMessage;
_aRouteRequest = Opcode.OuterRouteRequest;
_aRouteResponse = Opcode.OuterRouteResponse;
opCodeName = "OuterOpcode";
protoFile = $"{ProtoBufDefine.ProtoBufDirectory}OuterMessage.proto";
saveDirectory.Add(ProtoBufDefine.ServerDirectory, _serverTemplate);
saveDirectory.Add(ProtoBufDefine.ClientDirectory, _clientTemplate);
break;
}
case ProtoBufOpCodeType.Inner:
{
// 预留1000个协议号给框架内部协议用
_aMessage = Opcode.InnerMessage + 1000;
_aRequest = Opcode.InnerRequest + 1000;
_aResponse = Opcode.InnerResponse + 1000;
_aRouteMessage = Opcode.InnerRouteMessage + 1000;
_aRouteRequest = Opcode.InnerRouteRequest + 1000;
_aRouteResponse = Opcode.InnerRouteResponse + 1000;
opCodeName = "InnerOpcode";
protoFile = $"{ProtoBufDefine.ProtoBufDirectory}InnerMessage.proto";
saveDirectory.Add(ProtoBufDefine.ServerDirectory, _serverTemplate);
break;
}
case ProtoBufOpCodeType.InnerBson:
{
// 预留1000个协议号给框架内部协议用
_aMessage = Opcode.InnerBsonMessage + 1000;
_aRequest = Opcode.InnerBsonRequest + 1000;
_aResponse = Opcode.InnerBsonResponse + 1000;
_aRouteMessage = Opcode.InnerBsonRouteMessage + 1000;
_aRouteRequest = Opcode.InnerBsonRouteRequest + 1000;
_aRouteResponse = Opcode.InnerBsonRouteResponse + 1000;
opCodeName = "InnerBsonOpcode";
protoFile = $"{ProtoBufDefine.ProtoBufDirectory}InnerBsonMessage.proto";
saveDirectory.Add(ProtoBufDefine.ServerDirectory, _serverTemplate);
break;
}
}
var protoFileText = await File.ReadAllTextAsync(protoFile);
foreach (var line in protoFileText.Split('\n'))
{
var currentLine = line.Trim();
if (string.IsNullOrWhiteSpace(currentLine))
{
continue;
}
if (currentLine.StartsWith("///"))
{
file.AppendFormat(" /// <summary>\r\n" + " /// {0}\r\n" + " /// </summary>\r\n", currentLine.TrimStart(new[] {'/', '/', '/'}));
continue;
}
if (currentLine.StartsWith("message"))
{
isMsgHead = true;
opcodeInfo = new OpcodeInfo();
file.AppendLine("\t[ProtoContract]");
className = currentLine.Split(ProtoBufDefine.SplitChars, StringSplitOptions.RemoveEmptyEntries)[1];
var splits = currentLine.Split(new[] {"//"}, StringSplitOptions.RemoveEmptyEntries);
if (splits.Length > 1)
{
var parameterArray = currentLine.Split(new[] {"//"}, StringSplitOptions.RemoveEmptyEntries)[1].Trim().Split(',');
parameter = parameterArray[0].Trim();
switch (parameterArray.Length)
{
case 2:
responseTypeStr = parameterArray[1].Trim();
break;
case 3:
{
customRouteType = parameterArray[1].Trim();
if (parameterArray.Length == 3)
{
responseTypeStr = parameterArray[2].Trim();
}
break;
}
}
}
else
{
parameter = "";
}
file.Append(string.IsNullOrWhiteSpace(parameter)
? $"\tpublic partial class {className} : AProto"
: $"\tpublic partial class {className} : AProto, {parameter}");
opcodeInfo.Name = className;
continue;
}
if (!isMsgHead)
{
continue;
}
switch (currentLine)
{
case "{":
{
file.AppendLine("\n\t{");
if (string.IsNullOrWhiteSpace(parameter) || parameter == "IMessage")
{
opcodeInfo.Code += ++_aMessage;
file.AppendLine($"\t\tpublic uint OpCode() {{ return {opCodeName}.{className}; }}");
}
else
{
if (responseTypeStr != null)
{
file.AppendLine("\t\t[ProtoIgnore]");
file.AppendLine($"\t\tpublic {responseTypeStr} ResponseType {{ get; set; }}");
responseTypeStr = null;
}
else
{
if (parameter.Contains("RouteRequest"))
{
Exporter.LogError($"{opcodeInfo.Name} 没指定ResponseType");
}
}
file.AppendLine($"\t\tpublic uint OpCode() {{ return {opCodeName}.{className}; }}");
if (customRouteType != null)
{
file.AppendLine($"\t\tpublic long RouteTypeOpCode() {{ return (long)RouteType.{customRouteType}; }}");
customRouteType = null;
}
else if (parameter is "IAddressableRouteRequest" or "IAddressableRouteMessage")
{
file.AppendLine($"\t\tpublic long RouteTypeOpCode() {{ return CoreRouteType.Addressable; }}");
}
else if (parameter.EndsWith("BsonRouteMessage") || parameter.EndsWith("BsonRouteRequest"))
{
file.AppendLine($"\t\tpublic long RouteTypeOpCode() {{ return CoreRouteType.BsonRoute; }}");
}
else if (parameter is "IRouteMessage" or "IRouteRequest")
{
file.AppendLine($"\t\tpublic long RouteTypeOpCode() {{ return CoreRouteType.Route; }}");
}
switch (parameter)
{
case "IRequest":
case "IBsonRequest":
{
opcodeInfo.Code += ++_aRequest;
break;
}
case "IResponse":
case "IBsonResponse":
{
opcodeInfo.Code += ++_aResponse;
file.AppendLine("\t\t[ProtoMember(91, IsRequired = true)]");
file.AppendLine("\t\tpublic int ErrorCode { get; set; }");
break;
}
default:
{
if (parameter.EndsWith("RouteMessage") || parameter == "IRouteMessage")
{
opcodeInfo.Code += ++_aRouteMessage;
}
else if (parameter.EndsWith("RouteRequest") || parameter == "IRouteRequest")
{
opcodeInfo.Code += ++_aRouteRequest;
}
else if (parameter.EndsWith("RouteResponse") || parameter == "IRouteResponse")
{
opcodeInfo.Code += ++_aRouteResponse;
file.AppendLine("\t\t[ProtoMember(91, IsRequired = true)]");
file.AppendLine("\t\tpublic int ErrorCode { get; set; }");
}
break;
}
}
}
_opcodes.Add(opcodeInfo);
continue;
}
case "}":
{
isMsgHead = false;
file.AppendLine("\t}");
continue;
}
case "":
{
continue;
}
}
if (currentLine.StartsWith("//"))
{
file.AppendFormat("\t\t///<summary>\r\n" + "\t\t/// {0}\r\n" + "\t\t///</summary>\r\n", currentLine.TrimStart('/', '/'));
continue;
}
if (currentLine.StartsWith("repeated"))
{
Repeated(file, currentLine);
}
else
{
Members(file, currentLine);
}
}
var csName = $"{Path.GetFileNameWithoutExtension(protoFile)}.cs";
foreach (var (directory, template) in saveDirectory)
{
var csFile = Path.Combine(directory, csName);
var content = template.Replace("(Content)", file.ToString());
await File.WriteAllTextAsync(csFile, content);
}
file.Clear();
file.AppendLine("namespace TEngine");
file.AppendLine("{");
file.AppendLine($"\tpublic static partial class {opCodeName}");
file.AppendLine("\t{");
foreach (var opcode in _opcodes)
{
file.AppendLine($"\t\t public const int {opcode.Name} = {opcode.Code};");
}
_opcodes.Clear();
file.AppendLine("\t}");
file.AppendLine("}");
foreach (var (directory, _) in saveDirectory)
{
var csFile = Path.Combine(directory, $"{opCodeName}.cs");
await File.WriteAllTextAsync(csFile, file.ToString());
}
}
private async Task RouteType()
{
var routeTypeFile = $"{ProtoBufDefine.ProtoBufDirectory}RouteType.Config";
var protoFileText = await File.ReadAllTextAsync(routeTypeFile);
var routeTypeFileSb = new StringBuilder();
routeTypeFileSb.AppendLine("namespace TEngine.Core.Network\n{");
routeTypeFileSb.AppendLine("\t// Route协议定义(需要定义1000以上、因为1000以内的框架预留)\t");
routeTypeFileSb.AppendLine("\tpublic enum RouteType : long\n\t{");
foreach (var line in protoFileText.Split('\n'))
{
var currentLine = line.Trim();
if (currentLine.StartsWith("//"))
{
continue;
}
var splits = currentLine.Split(new[] {"//"}, StringSplitOptions.RemoveEmptyEntries);
var routeTypeStr = splits[0].Split("=", StringSplitOptions.RemoveEmptyEntries);
routeTypeFileSb.Append($"\t\t{routeTypeStr[0].Trim()} = {routeTypeStr[1].Trim()},");
if (splits.Length > 1)
{
routeTypeFileSb.Append($" // {splits[1].Trim()}\n");
}
else
{
routeTypeFileSb.Append('\n');
}
}
routeTypeFileSb.AppendLine("\t}\n}");
var file = routeTypeFileSb.ToString();
await File.WriteAllTextAsync($"{ProtoBufDefine.ServerDirectory}RouteType.cs", file);
await File.WriteAllTextAsync($"{ProtoBufDefine.ClientDirectory}RouteType.cs", file);
}
private void Repeated(StringBuilder file, string newline)
{
try
{
var index = newline.IndexOf(";", StringComparison.Ordinal);
newline = newline.Remove(index);
var property = newline.Split(ProtoBufDefine.SplitChars, StringSplitOptions.RemoveEmptyEntries);
var type = property[1];
var name = property[2];
var memberIndex = int.Parse(property[4]);
type = ConvertType(type);
file.AppendLine($"\t\t[ProtoMember({memberIndex})]");
file.AppendLine($"\t\tpublic List<{type}> {name} = new List<{type}>();");
}
catch (Exception e)
{
Exporter.LogError($"{newline}\n {e}");
}
}
private void Members(StringBuilder file, string currentLine)
{
try
{
var index = currentLine.IndexOf(";", StringComparison.Ordinal);
currentLine = currentLine.Remove(index);
var property = currentLine.Split(ProtoBufDefine.SplitChars, StringSplitOptions.RemoveEmptyEntries);
var type = property[0];
var name = property[1];
var memberIndex = int.Parse(property[3]);
var typeCs = ConvertType(type);
string defaultValue = GetDefault(typeCs);
file.AppendLine($"\t\t[ProtoMember({memberIndex})]");
file.AppendLine($"\t\tpublic {typeCs} {name} {{ get; set; }}");
}
catch (Exception e)
{
Exporter.LogError($"{currentLine}\n {e}");
}
}
private string ConvertType(string type)
{
return type switch
{
"int[]" => "int[] { }",
"int32[]" => "int[] { }",
"int64[]" => "long[] { }",
"int32" => "int",
"int64" => "long",
_ => type
};
}
private string GetDefault(string type)
{
type = type.Trim();
switch (type)
{
case "byte":
case "short":
case "int":
case "long":
case "float":
case "double":
return "0";
case "bool":
return "false";
default:
return "null";
}
}
private void LoadTemplate()
{
string[] lines = File.ReadAllLines(ProtoBufDefine.ProtoBufTemplatePath, Encoding.UTF8);
StringBuilder serverSb = new StringBuilder();
StringBuilder clientSb = new StringBuilder();
int flag = 0;
foreach (string line in lines)
{
string trim = line.Trim();
if (trim.StartsWith("#if") && trim.Contains("SERVER"))
{
flag = 1;
continue;
}
else if(trim.StartsWith("#else"))
{
flag = 2;
continue;
}
else if(trim.StartsWith($"#endif"))
{
flag = 0;
continue;
}
switch (flag)
{
case 1: // 服务端
{
serverSb.AppendLine(line);
break;
}
case 2: // 客户端
{
clientSb.AppendLine(line);
break;
}
default: // 双端
{
serverSb.AppendLine(line);
clientSb.AppendLine(line);
break;
}
}
}
_serverTemplate = serverSb.ToString();
_clientTemplate = clientSb.ToString();
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2876caa2819095b4e94fa1ca697e3fba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: