mirror of
https://github.com/Alex-Rachel/TEngine.git
synced 2025-08-14 16:51:28 +00:00
[+] TEngineServer
[+] TEngineServer
This commit is contained in:
8
Assets/GameScripts/DotNet/Core/Exporter/Excel.meta
Normal file
8
Assets/GameScripts/DotNet/Core/Exporter/Excel.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd2dcfcb8e4f2cd458f5b287c26f8dc4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/GameScripts/DotNet/Core/Exporter/Excel/Base.meta
Normal file
8
Assets/GameScripts/DotNet/Core/Exporter/Excel/Base.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 374826e14ee51d24b9386dcdba282238
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5ba993a045ee2d94da4827d5eff902af
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 85d5d45679512064fa1c6dbbbc5ff29c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0dac52ee40f938641a63d624ff19dd3f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae216cf2a8852c54d9b1de65bc64fafa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,9 @@
|
||||
#if TENGINE_NET
|
||||
namespace TEngine.Core;
|
||||
|
||||
public class ExportInfo
|
||||
{
|
||||
public string Name;
|
||||
public FileInfo FileInfo;
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3b19f3554768a6647b36f89e6a7c2116
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c7d1a9efd4b9a94bb7c4d99a7160068
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 30f0a5e8602ad3d4f83d9e275d1edd15
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
788
Assets/GameScripts/DotNet/Core/Exporter/Excel/ExcelExporter.cs
Normal file
788
Assets/GameScripts/DotNet/Core/Exporter/Excel/ExcelExporter.cs
Normal 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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 11f644344212d644dbfd6cae6ff1f133
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 04d66af120535af4a9dbbbf4e73931a1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
130
Assets/GameScripts/DotNet/Core/Exporter/Exporter.cs
Normal file
130
Assets/GameScripts/DotNet/Core/Exporter/Exporter.cs
Normal 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
|
11
Assets/GameScripts/DotNet/Core/Exporter/Exporter.cs.meta
Normal file
11
Assets/GameScripts/DotNet/Core/Exporter/Exporter.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2b2afd6fb4280f446aff438db7861e64
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/GameScripts/DotNet/Core/Exporter/Interface.meta
Normal file
8
Assets/GameScripts/DotNet/Core/Exporter/Interface.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3fa83b6f387771f48be26dc88c05e808
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,10 @@
|
||||
namespace TEngine.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// 表示是一个配置文件
|
||||
/// </summary>
|
||||
public interface IConfigTable
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f0c846012d7e36e4fa17c615f15da685
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d55723aa5c671d544a461eb8fb524769
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/GameScripts/DotNet/Core/Exporter/ProtoBuf.meta
Normal file
8
Assets/GameScripts/DotNet/Core/Exporter/ProtoBuf.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5c6c545584f5eb24892e7de1da9daffd
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d418e7f4b6646504c8a6ca4b381992b0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2876caa2819095b4e94fa1ca697e3fba
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user