提交导表相关功能
This commit is contained in:
25
Tools/ConfigBuilder/ConfigBuilder.sln
Normal file
25
Tools/ConfigBuilder/ConfigBuilder.sln
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.14.36408.4 d17.14
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NBConfigBuilder", "NBConfigBuilder\NBConfigBuilder.csproj", "{A6264F0E-870D-401D-B1BF-698695AC674C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A6264F0E-870D-401D-B1BF-698695AC674C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A6264F0E-870D-401D-B1BF-698695AC674C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A6264F0E-870D-401D-B1BF-698695AC674C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A6264F0E-870D-401D-B1BF-698695AC674C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {F223241C-FF74-4903-8F47-8A5A5A0FAC99}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
3
Tools/ConfigBuilder/ConfigBuilder.sln.DotSettings.user
Normal file
3
Tools/ConfigBuilder/ConfigBuilder.sln.DotSettings.user
Normal file
@@ -0,0 +1,3 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ALogManager_002Ecs_002Fl_003AC_0021_003FUsers_003FFIREBAT_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fc5462be99aec4b8dbd0143c73a99d4d1e0000_003F10_003Fa592ee39_003FLogManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANativeWindow_002Ecs_002Fl_003AC_0021_003FUsers_003FFIREBAT_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F257c19112c5249f9bf699efc998471b6cef910_003F44_003Fcc810871_003FNativeWindow_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
|
||||
20
Tools/ConfigBuilder/NBConfigBuilder/App.cs
Normal file
20
Tools/ConfigBuilder/NBConfigBuilder/App.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
namespace NBConfigBuilder;
|
||||
|
||||
// 添加配置类
|
||||
public class AppConfig
|
||||
{
|
||||
public string ExcelPath { get; set; }
|
||||
public string ClientPath { get; set; }
|
||||
public string ClientJsonPath { get; set; }
|
||||
public string ServerPath { get; set; }
|
||||
public string ServerJsonPath { get; set; }
|
||||
public bool GenClient { get; set; }
|
||||
public bool GenServer { get; set; }
|
||||
|
||||
public string ExcelVersionPath => Path.Combine(ExcelPath, "Version.txt");
|
||||
}
|
||||
|
||||
public static class App
|
||||
{
|
||||
public static AppConfig Config;
|
||||
}
|
||||
17
Tools/ConfigBuilder/NBConfigBuilder/Core/IPool.cs
Normal file
17
Tools/ConfigBuilder/NBConfigBuilder/Core/IPool.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace NBConfigBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// 实现了这个接口代表支持对象池
|
||||
/// </summary>
|
||||
public interface IPool
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否从池里创建的
|
||||
/// </summary>
|
||||
bool IsPool();
|
||||
/// <summary>
|
||||
/// 设置是否从池里创建的
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
void SetIsPool(bool isPool);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
namespace NBConfigBuilder;
|
||||
|
||||
public partial class IntDictionaryConfig
|
||||
{
|
||||
public Dictionary<int, int> Dic;
|
||||
public int this[int key] => GetValue(key);
|
||||
|
||||
public bool TryGetValue(int key, out int value)
|
||||
{
|
||||
value = default;
|
||||
|
||||
if (!Dic.ContainsKey(key))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
value = Dic[key];
|
||||
return true;
|
||||
}
|
||||
|
||||
private int GetValue(int key)
|
||||
{
|
||||
return Dic.TryGetValue(key, out var value) ? value : 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
using System.Buffers;
|
||||
|
||||
namespace NBConfigBuilder;
|
||||
|
||||
public enum MemoryStreamBufferSource
|
||||
{
|
||||
None = 0,
|
||||
Pack = 1,
|
||||
UnPack = 2,
|
||||
}
|
||||
|
||||
public sealed class MemoryStreamBuffer : MemoryStream, IBufferWriter<byte>
|
||||
{
|
||||
public MemoryStreamBufferSource MemoryStreamBufferSource;
|
||||
|
||||
public MemoryStreamBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
public MemoryStreamBuffer(MemoryStreamBufferSource memoryStreamBufferSource, int capacity) : base(capacity)
|
||||
{
|
||||
MemoryStreamBufferSource = memoryStreamBufferSource;
|
||||
}
|
||||
|
||||
public MemoryStreamBuffer(byte[] buffer) : base(buffer)
|
||||
{
|
||||
}
|
||||
|
||||
public void Advance(int count)
|
||||
{
|
||||
if (count < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(count), count, "The value of 'count' cannot be negative.");
|
||||
}
|
||||
|
||||
var newLength = Position + count;
|
||||
|
||||
if (newLength != Length)
|
||||
{
|
||||
SetLength(newLength);
|
||||
}
|
||||
|
||||
Position = newLength;
|
||||
}
|
||||
|
||||
public Memory<byte> GetMemory(int sizeHint = 0)
|
||||
{
|
||||
if (sizeHint < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(sizeHint), sizeHint,
|
||||
"The value of 'count' cannot be negative.");
|
||||
}
|
||||
|
||||
if (Length - Position <= sizeHint)
|
||||
{
|
||||
SetLength(Position + sizeHint);
|
||||
}
|
||||
|
||||
return new Memory<byte>(GetBuffer(), (int)Position, (int)(Length - Position));
|
||||
}
|
||||
|
||||
public Span<byte> GetSpan(int sizeHint = 0)
|
||||
{
|
||||
if (sizeHint < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(sizeHint), sizeHint,
|
||||
"The value of 'count' cannot be negative.");
|
||||
}
|
||||
|
||||
if (Length - Position <= sizeHint)
|
||||
{
|
||||
SetLength(Position + sizeHint);
|
||||
}
|
||||
|
||||
return new Span<byte>(GetBuffer(), (int)Position, (int)(Length - Position));
|
||||
}
|
||||
}
|
||||
217
Tools/ConfigBuilder/NBConfigBuilder/Core/OneToManyList.cs
Normal file
217
Tools/ConfigBuilder/NBConfigBuilder/Core/OneToManyList.cs
Normal file
@@ -0,0 +1,217 @@
|
||||
namespace NBConfigBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// 可回收的、一对多关系的列表池。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
public class OneToManyListPool<TKey, TValue> : OneToManyList<TKey, TValue>, IDisposable, IPool where TKey : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个 <see cref="OneToManyListPool{TKey, TValue}"/> 一对多关系的列表池的实例。
|
||||
/// </summary>
|
||||
/// <returns>创建的实例。</returns>
|
||||
public static OneToManyListPool<TKey, TValue> Create()
|
||||
{
|
||||
var list = Pool<OneToManyListPool<TKey, TValue>>.Rent();
|
||||
list._isDispose = false;
|
||||
list._isPool = true;
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放当前对象所占用的资源,并将对象回收到对象池中。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
Pool<OneToManyListPool<TKey, TValue>>.Return(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 一对多关系的列表字典。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
public class OneToManyList<TKey, TValue> : Dictionary<TKey, List<TValue>> where TKey : notnull
|
||||
{
|
||||
private readonly int _recyclingLimit = 120;
|
||||
private static readonly List<TValue> Empty = new List<TValue>();
|
||||
private readonly Queue<List<TValue>> _queue = new Queue<List<TValue>>();
|
||||
|
||||
/// <summary>
|
||||
/// 初始化一个新的 <see cref="OneToManyList{TKey, TValue}"/> 实例。
|
||||
/// </summary>
|
||||
public OneToManyList()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置最大缓存数量
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public OneToManyList(int recyclingLimit)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断给定的键和值是否存在于列表中。
|
||||
/// </summary>
|
||||
/// <param name="key">要搜索的键。</param>
|
||||
/// <param name="value">要搜索的值。</param>
|
||||
/// <returns>如果存在则为 <see langword="true"/>,否则为 <see langword="false"/>。</returns>
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向列表中添加指定键和值。
|
||||
/// </summary>
|
||||
/// <param name="key">要添加值的键。</param>
|
||||
/// <param name="value">要添加的值。</param>
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var list))
|
||||
{
|
||||
list = Fetch();
|
||||
list.Add(value);
|
||||
Add(key, list);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
list.Add(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定键对应的列表中的第一个值。
|
||||
/// </summary>
|
||||
/// <param name="key">要获取值的键。</param>
|
||||
/// <returns>键对应的列表中的第一个值。</returns>
|
||||
public TValue First(TKey key)
|
||||
{
|
||||
return !TryGetValue(key, out var list) ? default : list.FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从列表中移除指定键和值。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除值的键。</param>
|
||||
/// <param name="value">要移除的值。</param>
|
||||
/// <returns>如果成功移除则为 <see langword="true"/>,否则为 <see langword="false"/>。</returns>
|
||||
public bool RemoveValue(TKey key, TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var list))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var isRemove = list.Remove(value);
|
||||
|
||||
if (list.Count == 0)
|
||||
{
|
||||
isRemove = RemoveByKey(key);
|
||||
}
|
||||
|
||||
return isRemove;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从列表中移除指定键及其关联的所有值。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除的键。</param>
|
||||
/// <returns>如果成功移除则为 <see langword="true"/>,否则为 <see langword="false"/>。</returns>
|
||||
public bool RemoveByKey(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var list))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Remove(key);
|
||||
Recycle(list);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定键关联的所有值的列表。
|
||||
/// </summary>
|
||||
/// <param name="key">要获取值的键。</param>
|
||||
/// <returns>键关联的所有值的列表。</returns>
|
||||
public List<TValue> GetValues(TKey key)
|
||||
{
|
||||
if (TryGetValue(key, out List<TValue> list))
|
||||
{
|
||||
return list;
|
||||
}
|
||||
|
||||
return Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除字典中的所有键值对,并回收相关的值集合。
|
||||
/// </summary>
|
||||
public new void Clear()
|
||||
{
|
||||
foreach (var keyValuePair in this) Recycle(keyValuePair.Value);
|
||||
|
||||
base.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从空闲值集合队列中获取一个值集合,如果队列为空则创建一个新的值集合。
|
||||
/// </summary>
|
||||
/// <returns>从队列中获取的值集合。</returns>
|
||||
private List<TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new List<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回收一个不再使用的值集合到空闲值集合队列中。
|
||||
/// </summary>
|
||||
/// <param name="list">要回收的值集合。</param>
|
||||
private void Recycle(List<TValue> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||
|
||||
_queue.Enqueue(list);
|
||||
}
|
||||
}
|
||||
72
Tools/ConfigBuilder/NBConfigBuilder/Core/Pool.cs
Normal file
72
Tools/ConfigBuilder/NBConfigBuilder/Core/Pool.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
namespace NBConfigBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// 静态的对象池系统,不支持多线程。
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public static class Pool<T> where T : IPool, new()
|
||||
{
|
||||
private static readonly Queue<T> PoolQueue = new Queue<T>();
|
||||
|
||||
/// <summary>
|
||||
/// 池子里可用的数量
|
||||
/// </summary>
|
||||
public static int Count => PoolQueue.Count;
|
||||
|
||||
/// <summary>
|
||||
/// 租借
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static T Rent()
|
||||
{
|
||||
return PoolQueue.Count == 0 ? new T() : PoolQueue.Dequeue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 租借
|
||||
/// </summary>
|
||||
/// <param name="generator">如果池子里没有,会先执行这个委托。</param>
|
||||
/// <returns></returns>
|
||||
public static T Rent(Func<T> generator)
|
||||
{
|
||||
return PoolQueue.Count == 0 ? generator() : PoolQueue.Dequeue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返还
|
||||
/// </summary>
|
||||
/// <param name="t"></param>
|
||||
public static void Return(T t)
|
||||
{
|
||||
if (t == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PoolQueue.Enqueue(t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返还
|
||||
/// </summary>
|
||||
/// <param name="t">返还的东西</param>
|
||||
/// <param name="reset">返还后执行的委托</param>
|
||||
public static void Return(T t, Action<T> reset)
|
||||
{
|
||||
if (t == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
reset(t);
|
||||
PoolQueue.Enqueue(t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空池子
|
||||
/// </summary>
|
||||
public static void Clear()
|
||||
{
|
||||
PoolQueue.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Fantasy.Serialize;
|
||||
|
||||
public abstract class ASerialize : ISupportInitialize, IDisposable
|
||||
{
|
||||
public virtual void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void BeginInit()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void EndInit()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void AfterDeserialization() => EndInit();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
namespace NBConfigBuilder;
|
||||
|
||||
public sealed partial class StringDictionaryConfig
|
||||
{
|
||||
public Dictionary<int, string> Dic;
|
||||
public string this[int key] => GetValue(key);
|
||||
|
||||
public bool TryGetValue(int key, out string value)
|
||||
{
|
||||
value = default;
|
||||
|
||||
if (!Dic.ContainsKey(key))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
value = Dic[key];
|
||||
return true;
|
||||
}
|
||||
|
||||
private string GetValue(int key)
|
||||
{
|
||||
return Dic.TryGetValue(key, out var value) ? value : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
using System.Reflection;
|
||||
using Fantasy.Serialize;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using ProtoBuf;
|
||||
|
||||
namespace NBConfigBuilder;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 动态程序集类,用于加载动态生成的程序集并获取动态信息。
|
||||
/// </summary>
|
||||
public static class DynamicAssembly
|
||||
{
|
||||
private static void MetadataReference(out string assemblyName, out List<MetadataReference> metadataReferenceList)
|
||||
{
|
||||
AssemblyMetadata assemblyMetadata;
|
||||
MetadataReference metadataReference;
|
||||
var currentDomain = AppDomain.CurrentDomain;
|
||||
assemblyName = Path.GetRandomFileName();
|
||||
var assemblyArray = currentDomain.GetAssemblies();
|
||||
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);
|
||||
}
|
||||
|
||||
// 添加Proto支持
|
||||
|
||||
assemblyMetadata = AssemblyMetadata.CreateFromFile(typeof(ProtoMemberAttribute).Assembly.Location);
|
||||
metadataReference = assemblyMetadata.GetReference();
|
||||
metadataReferenceList.Add(metadataReference);
|
||||
|
||||
// 添加Fantasy支持
|
||||
|
||||
assemblyMetadata = AssemblyMetadata.CreateFromFile(typeof(ASerialize).Assembly.Location);
|
||||
metadataReference = assemblyMetadata.GetReference();
|
||||
metadataReferenceList.Add(metadataReference);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载指定路径下的动态程序集。
|
||||
/// </summary>
|
||||
/// <param name="path">程序集文件路径。</param>
|
||||
/// <returns>加载的动态程序集。</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
// 注册程序集
|
||||
MetadataReference(out var assemblyName, out var metadataReferenceList);
|
||||
var 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)
|
||||
{
|
||||
Log.Error(resultDiagnostic.GetMessage());
|
||||
}
|
||||
|
||||
throw new Exception("failures");
|
||||
}
|
||||
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
return Assembly.Load(ms.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取动态程序集中指定表格的动态信息。
|
||||
/// </summary>
|
||||
/// <param name="dynamicAssembly">动态程序集。</param>
|
||||
/// <param name="tableName">表格名称。</param>
|
||||
/// <returns>动态信息对象。</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据类型名称获取动态类型。
|
||||
/// </summary>
|
||||
/// <param name="dynamicAssembly">动态程序集。</param>
|
||||
/// <param name="typeName">类型名称。</param>
|
||||
/// <returns>动态类型。</returns>
|
||||
private static Type GetConfigType(Assembly dynamicAssembly, string typeName)
|
||||
{
|
||||
var configType = dynamicAssembly.GetType($"Fantasy.{typeName}");
|
||||
|
||||
if (configType == null)
|
||||
{
|
||||
throw new FileNotFoundException($"Fantasy.{typeName} not found");
|
||||
}
|
||||
|
||||
return configType;
|
||||
// return dynamicAssembly.GetType($"Fantasy.{typeName}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建动态实例。
|
||||
/// </summary>
|
||||
/// <param name="configType">动态类型。</param>
|
||||
/// <returns>动态实例。</returns>
|
||||
public static object CreateInstance(Type configType)
|
||||
{
|
||||
var config = Activator.CreateInstance(configType);
|
||||
|
||||
if (config == null)
|
||||
{
|
||||
throw new Exception($"{configType.Name} is Activator.CreateInstance error");
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace NBConfigBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// 动态配置数据类型类,用于存储动态配置数据的相关信息。
|
||||
/// </summary>
|
||||
public class DynamicConfigDataType
|
||||
{
|
||||
/// <summary>
|
||||
/// 配置数据对象,继承自 AProto 基类。
|
||||
/// </summary>
|
||||
public object ConfigData;
|
||||
/// <summary>
|
||||
/// 配置数据类型。
|
||||
/// </summary>
|
||||
public Type ConfigDataType;
|
||||
/// <summary>
|
||||
/// 配置类型。
|
||||
/// </summary>
|
||||
public Type ConfigType;
|
||||
/// <summary>
|
||||
/// 反射方法信息,用于调用特定方法。
|
||||
/// </summary>
|
||||
public MethodInfo Method;
|
||||
/// <summary>
|
||||
/// 配置数据对象实例。
|
||||
/// </summary>
|
||||
public object Obj;
|
||||
/// <summary>
|
||||
/// 用于生成 JSON 格式数据的字符串构建器。
|
||||
/// </summary>
|
||||
public StringBuilder Json = new StringBuilder();
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using System.Reflection;
|
||||
using Fantasy.Serialize;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using ProtoBuf;
|
||||
|
||||
namespace NBConfigBuilder;
|
||||
|
||||
public class OneDynamicAssembly
|
||||
{
|
||||
private readonly List<SyntaxTree> _syntaxTreeList = new List<SyntaxTree>();
|
||||
|
||||
public void Load(string file)
|
||||
{
|
||||
using var fileStream = new StreamReader(file);
|
||||
var cSharp = CSharpSyntaxTree.ParseText(fileStream.ReadToEnd());
|
||||
_syntaxTreeList.Add(cSharp);
|
||||
}
|
||||
|
||||
public Assembly Assembly
|
||||
{
|
||||
get
|
||||
{
|
||||
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(ASerialize).Assembly.Location);
|
||||
metadataReference = assemblyMetadata.GetReference();
|
||||
metadataReferenceList.Add(metadataReference);
|
||||
// 添加MessagePack支持
|
||||
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)
|
||||
{
|
||||
Log.Error(resultDiagnostic.GetMessage());
|
||||
}
|
||||
|
||||
throw new Exception("failures");
|
||||
}
|
||||
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
return Assembly.Load(ms.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
950
Tools/ConfigBuilder/NBConfigBuilder/Exporter/ExcelExporter.cs
Normal file
950
Tools/ConfigBuilder/NBConfigBuilder/Exporter/ExcelExporter.cs
Normal file
@@ -0,0 +1,950 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.VisualBasic.ApplicationServices;
|
||||
using Newtonsoft.Json;
|
||||
using OfficeOpenXml;
|
||||
using static System.String;
|
||||
|
||||
namespace NBConfigBuilder;
|
||||
|
||||
using TableDictionary = SortedDictionary<string, List<int>>;
|
||||
|
||||
/// <summary>
|
||||
/// Excel 数据导出器,用于从 Excel 文件导出数据到二进制格式和生成 C# 类文件。
|
||||
/// </summary>
|
||||
public sealed class ExcelExporter
|
||||
{
|
||||
public ExportType ExportType;
|
||||
private readonly string _excelProgramPath;
|
||||
private readonly string _versionFilePath;
|
||||
|
||||
// private readonly string _excelClientFileDirectory;
|
||||
// private readonly string _excelServerFileDirectory;
|
||||
// private readonly string _excelServerJsonDirectory;
|
||||
// private readonly string _excelClientJsonDirectory;
|
||||
|
||||
public VersionInfo VersionInfo; // 存储 Excel 文件的版本信息。
|
||||
private readonly Regex _regexName = new Regex("^[a-zA-Z][a-zA-Z0-9_]*$"); // 用于验证 Excel 表名的正则表达式。
|
||||
private readonly HashSet<string> _loadFiles = [".xlsx", ".xlsm", ".csv"]; // 加载的支持文件扩展名。
|
||||
|
||||
private readonly OneToManyList<string, ExportInfo>
|
||||
_tables = new OneToManyList<string, ExportInfo>(); // 存储 Excel 表及其导出信息。
|
||||
|
||||
private readonly ConcurrentDictionary<string, ExcelTable> _excelTables =
|
||||
new ConcurrentDictionary<string, ExcelTable>(); // 存储解析后的 Excel 表。
|
||||
|
||||
public readonly ConcurrentDictionary<string, ExcelWorksheet> Worksheets =
|
||||
new ConcurrentDictionary<string, ExcelWorksheet>(); // 存储已加载的 Excel 工作表。
|
||||
|
||||
public readonly Dictionary<string, string> IgnoreTable = new Dictionary<string, string>(); // 存储以#开头的的表和路径
|
||||
|
||||
/// <summary>
|
||||
/// 导表支持的数据类型集合。
|
||||
/// </summary>
|
||||
public static readonly HashSet<string> ColTypeSet =
|
||||
[
|
||||
"", "0", "bool", "byte", "short", "ushort", "int", "uint", "long", "ulong", "float", "string",
|
||||
"IntDictionaryConfig", "StringDictionaryConfig",
|
||||
"short[]", "int[]", "long[]", "float[]", "string[]", "uint[]"
|
||||
];
|
||||
|
||||
static ExcelExporter()
|
||||
{
|
||||
ExcelPackage.License.SetNonCommercialOrganization("Fantasy");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据指定的 exportType 初始化 ExcelExporter 类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="exportType">要执行的导出类型(AllExcel 或 AllExcelIncrement)。</param>
|
||||
public ExcelExporter(ExportType exportType)
|
||||
{
|
||||
ExportType = exportType;
|
||||
|
||||
if (App.Config.ExcelVersionPath == null || App.Config.ExcelVersionPath.Trim() == "")
|
||||
{
|
||||
Log.Info($"ExcelVersionFile Can not be empty!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (App.Config.ExcelPath == null || App.Config.ExcelPath.Trim() == "")
|
||||
{
|
||||
Log.Info($"ExcelProgramPath Can not be empty!");
|
||||
return;
|
||||
}
|
||||
|
||||
_excelProgramPath = FileHelper.GetFullPath(App.Config.ExcelPath);
|
||||
_versionFilePath = FileHelper.GetFullPath(App.Config.ExcelVersionPath);
|
||||
|
||||
if (App.Config.GenClient)
|
||||
{
|
||||
if (App.Config.ClientJsonPath == null ||
|
||||
App.Config.ClientJsonPath.Trim() == "")
|
||||
{
|
||||
Log.Info($"ExcelClientJsonDirectory Can not be empty!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (App.Config.ClientPath == null ||
|
||||
App.Config.ClientPath.Trim() == "")
|
||||
{
|
||||
Log.Info($"ExcelServerFileDirectory Can not be empty!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (App.Config.GenServer)
|
||||
{
|
||||
if (App.Config.ServerPath == null ||
|
||||
App.Config.ServerPath.Trim() == "")
|
||||
{
|
||||
Log.Info($"ExcelServerFileDirectory Can not be empty!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (App.Config.ServerJsonPath == null ||
|
||||
App.Config.ServerJsonPath.Trim() == "")
|
||||
{
|
||||
Log.Info($"ExcelServerJsonDirectory Can not be empty!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (ExportType)
|
||||
{
|
||||
case ExportType.AllExcelIncrement:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case ExportType.AllExcel:
|
||||
{
|
||||
if (File.Exists(_versionFilePath))
|
||||
{
|
||||
File.Delete(_versionFilePath);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// SerializerManager.Initialize();
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
Find();
|
||||
Parsing();
|
||||
ExportToBinary();
|
||||
File.WriteAllText(_versionFilePath, JsonConvert.SerializeObject(VersionInfo));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找配置文件
|
||||
/// </summary>
|
||||
private void Find()
|
||||
{
|
||||
VersionInfo = File.Exists(_versionFilePath)
|
||||
? JsonConvert.DeserializeObject<VersionInfo>(File.ReadAllText(_versionFilePath))
|
||||
: new VersionInfo();
|
||||
|
||||
var dir = new DirectoryInfo(_excelProgramPath);
|
||||
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))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 如果文件名以#开头,那么这个文件夹下的所有文件都不导出
|
||||
|
||||
if (excelName.StartsWith("#", StringComparison.Ordinal))
|
||||
{
|
||||
IgnoreTable.Add(excelName, fullName);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 如果文件夹名包含#,那么这个文件夹下的所有文件都不导出
|
||||
|
||||
if (path.Contains("#", StringComparison.Ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_regexName.IsMatch(excelName))
|
||||
{
|
||||
Log.Info($"{excelName} 配置文件名非法");
|
||||
continue;
|
||||
}
|
||||
|
||||
var exportInfo = new ExportInfo()
|
||||
{
|
||||
Name = excelName, FileInfo = excelFile
|
||||
};
|
||||
|
||||
_tables.Add(excelName.Split('_')[0], exportInfo);
|
||||
}
|
||||
|
||||
var removeTables = new List<string>();
|
||||
|
||||
foreach (var (tableName, tableList) in _tables)
|
||||
{
|
||||
var isNeedExport = false;
|
||||
|
||||
foreach (var exportInfo in tableList)
|
||||
{
|
||||
var key = HashCodeHelper.ComputeHash64(exportInfo.Name);
|
||||
var timer = TimeHelper.Transition(exportInfo.FileInfo.LastWriteTime);
|
||||
|
||||
if (!isNeedExport)
|
||||
{
|
||||
if (VersionInfo.Tables.TryGetValue(key, out var lastWriteTime))
|
||||
{
|
||||
isNeedExport = lastWriteTime != timer;
|
||||
}
|
||||
else
|
||||
{
|
||||
isNeedExport = true;
|
||||
}
|
||||
}
|
||||
|
||||
VersionInfo.Tables[key] = 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 = worksheet.GetCellValue(5, col);
|
||||
if (colName.StartsWith("#", StringComparison.Ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 数值列不参与导出
|
||||
|
||||
var numericalCol = worksheet.GetCellValue(3, col);
|
||||
if (numericalCol != "" && numericalCol != "0")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var serverType = worksheet.GetCellValue(1, col);
|
||||
var clientType = worksheet.GetCellValue(2, col);
|
||||
var isExportServer = !IsNullOrEmpty(serverType) && serverType != "0";
|
||||
var isExportClient = !IsNullOrEmpty(clientType) && clientType != "0";
|
||||
|
||||
if (!isExportServer && !isExportClient)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isExportServer && isExportClient & serverType != clientType)
|
||||
{
|
||||
Log.Info(
|
||||
$"配置表 {exportInfo.Name} {col} 列 [{colName}] 客户端类型 {clientType} 和 服务端类型 {serverType} 不一致");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ColTypeSet.Contains(serverType) || !ColTypeSet.Contains(clientType))
|
||||
{
|
||||
Log.Info(
|
||||
$"配置表 {exportInfo.Name} {col} 列 [{colName}] 客户端类型 {clientType}, 服务端类型 {serverType} 不合法");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_regexName.IsMatch(colName))
|
||||
{
|
||||
Log.Info($"配置表 {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)
|
||||
{
|
||||
Log.Error($"Config : {tableName}, Name : {exportInfo.Name}, Error : {e}");
|
||||
}
|
||||
}
|
||||
|
||||
// 生成cs文件
|
||||
|
||||
if (App.Config.GenServer)
|
||||
{
|
||||
writeToClassTask.Add(Task.Run(() =>
|
||||
{
|
||||
WriteToClass(excelTable.ServerColInfos, App.Config.ServerPath, true);
|
||||
}));
|
||||
}
|
||||
|
||||
if (App.Config.GenClient)
|
||||
{
|
||||
writeToClassTask.Add(Task.Run(() =>
|
||||
{
|
||||
WriteToClass(excelTable.ClientColInfos, App.Config.ClientPath, false);
|
||||
}));
|
||||
}
|
||||
|
||||
Task.WaitAll(writeToClassTask.ToArray());
|
||||
_excelTables.TryAdd(tableName, excelTable);
|
||||
});
|
||||
|
||||
generateTasks.Add(task);
|
||||
}
|
||||
|
||||
Task.WaitAll(generateTasks.ToArray());
|
||||
Console.WriteLine("build success===");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 把数据和实体类转换二进制导出到文件中
|
||||
/// </summary>
|
||||
private void ExportToBinary()
|
||||
{
|
||||
System.Reflection.Assembly dynamicServerAssembly = null;
|
||||
System.Reflection.Assembly dynamicClientAssembly = null;
|
||||
var exportToBinaryTasks = new List<Task>();
|
||||
|
||||
if (App.Config.GenServer &&
|
||||
Directory.Exists(App.Config.ServerPath))
|
||||
{
|
||||
dynamicServerAssembly = DynamicAssembly.Load(App.Config.ServerPath);
|
||||
}
|
||||
|
||||
if (App.Config.GenClient &&
|
||||
Directory.Exists(App.Config.ClientPath))
|
||||
{
|
||||
dynamicClientAssembly = DynamicAssembly.Load(App.Config.ClientPath);
|
||||
}
|
||||
|
||||
foreach (var (tableName, tableList) in _tables)
|
||||
{
|
||||
var task = Task.Run(() =>
|
||||
{
|
||||
DynamicConfigDataType serverDynamicInfo = null;
|
||||
DynamicConfigDataType clientDynamicInfo = null;
|
||||
|
||||
var idCheck = new HashSet<string>();
|
||||
var excelTable = _excelTables[tableName];
|
||||
var csName = Path.GetFileNameWithoutExtension(tableName);
|
||||
|
||||
if (App.Config.GenServer)
|
||||
{
|
||||
var serverColInfoCount = excelTable.ServerColInfos.Sum(d => d.Value.Count);
|
||||
serverDynamicInfo = serverColInfoCount == 0
|
||||
? null
|
||||
: DynamicAssembly.GetDynamicInfo(dynamicServerAssembly, csName);
|
||||
}
|
||||
|
||||
if (App.Config.GenClient)
|
||||
{
|
||||
var clientColInfoCount = excelTable.ClientColInfos.Sum(d => d.Value.Count);
|
||||
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 (excelWorksheet.GetCellValue(row, 1).StartsWith("#", StringComparison.Ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var id = excelWorksheet.GetCellValue(row, 3);
|
||||
|
||||
if (idCheck.Contains(id))
|
||||
{
|
||||
Log.Info($"{tableListName.Name} 存在重复Id {id} 行号 {row}");
|
||||
continue;
|
||||
}
|
||||
|
||||
idCheck.Add(id);
|
||||
var isLast = row == rows && (i == tableList.Count - 1);
|
||||
|
||||
if (App.Config.GenServer)
|
||||
{
|
||||
GenerateBinary(fileInfoFullName, excelWorksheet, serverDynamicInfo, serverCols, id, row,
|
||||
isLast, true);
|
||||
}
|
||||
|
||||
if (App.Config.GenClient)
|
||||
{
|
||||
GenerateBinary(fileInfoFullName, excelWorksheet, clientDynamicInfo, clientCols, id, row,
|
||||
isLast, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"Table:{tableListName} error! \n{e}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
if (serverDynamicInfo?.ConfigData != null)
|
||||
{
|
||||
// var memoryStream = new MemoryStreamBuffer();
|
||||
// SerializerManager.GetSerializer(FantasySerializerType.ProtoBuf)
|
||||
// .Serialize(serverDynamicInfo.ConfigData, memoryStream);
|
||||
// if (!Directory.Exists(_excelServerBinaryDirectory))
|
||||
// {
|
||||
// Directory.CreateDirectory(_excelServerBinaryDirectory);
|
||||
// }
|
||||
|
||||
// var asSpan = memoryStream.GetBuffer().AsSpan(0, (int)memoryStream.Position);
|
||||
// File.WriteAllBytes(Path.Combine(_excelServerBinaryDirectory, $"{csName}Data.bytes"),
|
||||
// asSpan.ToArray());
|
||||
|
||||
if (serverDynamicInfo.Json.Length > 0)
|
||||
{
|
||||
if (!Directory.Exists(App.Config.ServerJsonPath))
|
||||
{
|
||||
Directory.CreateDirectory(App.Config.ServerJsonPath);
|
||||
}
|
||||
|
||||
using var sw = new StreamWriter(Path.Combine(App.Config.ServerJsonPath, $"{csName}Data.Json"));
|
||||
sw.WriteLine("{\"List\":[");
|
||||
sw.Write(serverDynamicInfo.Json.ToString());
|
||||
sw.WriteLine("]}");
|
||||
}
|
||||
}
|
||||
|
||||
if (clientDynamicInfo?.ConfigData != null)
|
||||
{
|
||||
// var memoryStream = new MemoryStreamBuffer();
|
||||
// SerializerManager.GetSerializer(FantasySerializerType.ProtoBuf)
|
||||
// .Serialize(clientDynamicInfo.ConfigData, memoryStream);
|
||||
// if (!Directory.Exists(_excelClientBinaryDirectory))
|
||||
// {
|
||||
// Directory.CreateDirectory(_excelClientBinaryDirectory);
|
||||
// }
|
||||
//
|
||||
// var asSpan = memoryStream.GetBuffer().AsSpan(0, (int)memoryStream.Position);
|
||||
// File.WriteAllBytes(Path.Combine(_excelClientBinaryDirectory, $"{csName}Data.bytes"),
|
||||
// asSpan.ToArray());
|
||||
|
||||
if (clientDynamicInfo.Json.Length > 0)
|
||||
{
|
||||
if (!Directory.Exists(App.Config.ClientJsonPath))
|
||||
{
|
||||
Directory.CreateDirectory(App.Config.ClientJsonPath);
|
||||
}
|
||||
|
||||
using var sw = new StreamWriter(Path.Combine(App.Config.ClientJsonPath, $"{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 = excelWorksheet.GetCellValue(5, colIndex);
|
||||
var value = excelWorksheet.GetCellValue(row, colIndex);
|
||||
|
||||
if (isServer)
|
||||
{
|
||||
colType = excelWorksheet.GetCellValue(1, colIndex);
|
||||
|
||||
if (IsNullOrEmpty(colType) || colType == "0")
|
||||
{
|
||||
colType = excelWorksheet.GetCellValue(2, colIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
colType = excelWorksheet.GetCellValue(2, colIndex);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
SetNewValue(dynamicInfo.ConfigType.GetProperty(colName), config, colType, value);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(
|
||||
$"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},");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 从 Excel 文件加载工作表并返回 ExcelWorksheet 对象。
|
||||
/// </summary>
|
||||
/// <param name="name">工作表的名称或文件路径。</param>
|
||||
/// <param name="isAddToDic">是否将加载的工作表添加到缓存字典中。</param>
|
||||
/// <returns>表示 Excel 工作表的 ExcelWorksheet 对象。</returns>
|
||||
public ExcelWorksheet LoadExcel(string name, bool isAddToDic)
|
||||
{
|
||||
if (Worksheets.TryGetValue(name, out var worksheet))
|
||||
{
|
||||
return worksheet;
|
||||
}
|
||||
|
||||
var workbookWorksheets = ExcelHelper.LoadExcel(name).Workbook.Worksheets;
|
||||
worksheet = workbookWorksheets[0];
|
||||
|
||||
if (isAddToDic)
|
||||
{
|
||||
Worksheets.TryAdd(name, worksheet);
|
||||
|
||||
foreach (var workbookWorksheet in workbookWorksheets)
|
||||
{
|
||||
try
|
||||
{
|
||||
var hash = HashCodeHelper.ComputeHash64(workbookWorksheet.Name);
|
||||
VersionInfo.WorksheetNames.Add(hash);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
|
||||
Worksheets.TryAdd(workbookWorksheet.Name, workbookWorksheet);
|
||||
}
|
||||
}
|
||||
|
||||
Log.Info(name);
|
||||
return workbookWorksheets[0];
|
||||
}
|
||||
|
||||
/// <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 = excelWorksheet.GetCellValue(5, colIndex);
|
||||
|
||||
if (colNameSet.Contains(colName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
colNameSet.Add(colName);
|
||||
|
||||
string colType;
|
||||
|
||||
if (isServer)
|
||||
{
|
||||
colType = excelWorksheet.GetCellValue(1, colIndex);
|
||||
|
||||
if (IsNullOrEmpty(colType) || colType == "0")
|
||||
{
|
||||
colType = excelWorksheet.GetCellValue(2, colIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
colType = excelWorksheet.GetCellValue(2, colIndex);
|
||||
}
|
||||
|
||||
var remarks = excelWorksheet.GetCellValue(4, colIndex);
|
||||
|
||||
// 解决不同平台换行符不一致的问题
|
||||
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
{
|
||||
fileBuilder.Append($"\r\n\t\t[ProtoMember({++index})]\r\n");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
fileBuilder.Append($"\n\t\t[ProtoMember({++index})]\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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 = ExcelTemplate.Template;
|
||||
|
||||
if (fileBuilder.Length > 0)
|
||||
{
|
||||
if (!Directory.Exists(exportPath))
|
||||
{
|
||||
FileHelper.CreateDirectory(exportPath);
|
||||
}
|
||||
|
||||
var content = template.Replace("(namespace)", "Fantasy")
|
||||
.Replace("(ConfigName)", csName)
|
||||
.Replace("(Fields)", fileBuilder.ToString());
|
||||
File.WriteAllText(Path.Combine(exportPath, $"{csName}.cs"), content);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetNewValue(PropertyInfo propertyInfo, object 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 "uint[]":
|
||||
{
|
||||
if (value != "0")
|
||||
{
|
||||
propertyInfo.SetValue(config, value.Split(",").Select(d => Convert.ToUInt32(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 "IntDictionaryConfig":
|
||||
{
|
||||
if (value.Trim() == "" || value.Trim() == "{}")
|
||||
{
|
||||
propertyInfo.SetValue(config, null);
|
||||
return;
|
||||
}
|
||||
|
||||
var attr = new IntDictionaryConfig { Dic = JsonConvert.DeserializeObject<Dictionary<int, int>>(value) };
|
||||
|
||||
propertyInfo.SetValue(config, attr);
|
||||
|
||||
return;
|
||||
}
|
||||
case "StringDictionaryConfig":
|
||||
{
|
||||
if (value.Trim() == "" || value.Trim() == "{}")
|
||||
{
|
||||
propertyInfo.SetValue(config, null);
|
||||
return;
|
||||
}
|
||||
|
||||
var attr = new StringDictionaryConfig
|
||||
{ Dic = JsonConvert.DeserializeObject<Dictionary<int, string>>(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;
|
||||
}
|
||||
}
|
||||
47
Tools/ConfigBuilder/NBConfigBuilder/Exporter/ExcelHelper.cs
Normal file
47
Tools/ConfigBuilder/NBConfigBuilder/Exporter/ExcelHelper.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using OfficeOpenXml;
|
||||
|
||||
namespace NBConfigBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// 提供操作 Excel 文件的辅助方法。
|
||||
/// </summary>
|
||||
public static class ExcelHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 加载 Excel 文件并返回 ExcelPackage 实例。
|
||||
/// </summary>
|
||||
/// <param name="name">Excel 文件的路径。</param>
|
||||
/// <returns>ExcelPackage 实例。</returns>
|
||||
public static ExcelPackage LoadExcel(string name)
|
||||
{
|
||||
return new ExcelPackage(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定工作表中指定行列位置的单元格值。
|
||||
/// </summary>
|
||||
/// <param name="sheet">Excel 工作表。</param>
|
||||
/// <param name="row">行索引。</param>
|
||||
/// <param name="column">列索引。</param>
|
||||
/// <returns>单元格值。</returns>
|
||||
public static string GetCellValue(this ExcelWorksheet sheet, int row, int column)
|
||||
{
|
||||
ExcelRange cell = sheet.Cells[row, column];
|
||||
|
||||
try
|
||||
{
|
||||
if (cell.Value == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
string s = cell.GetValue<string>();
|
||||
|
||||
return s.Trim();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception($"Rows {row} Columns {column} Content {cell.Text} {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
28
Tools/ConfigBuilder/NBConfigBuilder/Exporter/ExcelTable.cs
Normal file
28
Tools/ConfigBuilder/NBConfigBuilder/Exporter/ExcelTable.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
namespace NBConfigBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// Excel表格类,用于存储表格的名称和列信息。
|
||||
/// </summary>
|
||||
public sealed class ExcelTable
|
||||
{
|
||||
/// <summary>
|
||||
/// 表格的名称。
|
||||
/// </summary>
|
||||
public readonly string Name;
|
||||
/// <summary>
|
||||
/// 客户端列信息,使用排序字典存储列名和列索引列表。
|
||||
/// </summary>
|
||||
public readonly SortedDictionary<string, List<int>> ClientColInfos = new();
|
||||
/// <summary>
|
||||
/// 服务器端列信息,使用排序字典存储列名和列索引列表。
|
||||
/// </summary>
|
||||
public readonly SortedDictionary<string, List<int>> ServerColInfos = new();
|
||||
/// <summary>
|
||||
/// 构造函数,初始化Excel表格对象并设置表格名称。
|
||||
/// </summary>
|
||||
/// <param name="name">表格名称。</param>
|
||||
public ExcelTable(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
namespace NBConfigBuilder;
|
||||
|
||||
public static class ExcelTemplate
|
||||
{
|
||||
public static readonly string Template = """
|
||||
using System;
|
||||
using ProtoBuf;
|
||||
using Fantasy;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using Fantasy.ConfigTable;
|
||||
using Fantasy.Serialize;
|
||||
// ReSharper disable CollectionNeverUpdated.Global
|
||||
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
#pragma warning disable CS0169
|
||||
#pragma warning disable CS8618
|
||||
#pragma warning disable CS8625
|
||||
#pragma warning disable CS8603
|
||||
|
||||
namespace (namespace)
|
||||
{
|
||||
[ProtoContract]
|
||||
public sealed partial class (ConfigName)Data : ASerialize, IConfigTable, IProto
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public List<(ConfigName)> List { get; set; } = new List<(ConfigName)>();
|
||||
#if FANTASY_NET
|
||||
[ProtoIgnore]
|
||||
private readonly ConcurrentDictionary<uint, (ConfigName)> _configs = new ConcurrentDictionary<uint, (ConfigName)>();
|
||||
#else
|
||||
[ProtoIgnore]
|
||||
private readonly Dictionary<uint, (ConfigName)> _configs = new Dictionary<uint, (ConfigName)>();
|
||||
#endif
|
||||
private static (ConfigName)Data _instance = null;
|
||||
|
||||
public static (ConfigName)Data Instance
|
||||
{
|
||||
get { return _instance ??= ConfigTableHelper.Load<(ConfigName)Data>(); }
|
||||
private set => _instance = value;
|
||||
}
|
||||
|
||||
public (ConfigName) Get(uint id, bool check = true)
|
||||
{
|
||||
if (_configs.ContainsKey(id))
|
||||
{
|
||||
return _configs[id];
|
||||
}
|
||||
|
||||
if (check)
|
||||
{
|
||||
throw new Exception($"(ConfigName) not find {id} Id");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
public bool TryGet(uint id, out (ConfigName) config)
|
||||
{
|
||||
config = null;
|
||||
|
||||
if (!_configs.ContainsKey(id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
config = _configs[id];
|
||||
return true;
|
||||
}
|
||||
public override void AfterDeserialization()
|
||||
{
|
||||
foreach (var config in List)
|
||||
{
|
||||
#if FANTASY_NET
|
||||
_configs.TryAdd(config.Id, config);
|
||||
#else
|
||||
_configs.Add(config.Id, config);
|
||||
#endif
|
||||
config.AfterDeserialization();
|
||||
}
|
||||
|
||||
EndInit();
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
Instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public sealed partial class (ConfigName) : ASerialize, IProto
|
||||
{(Fields)
|
||||
}
|
||||
}
|
||||
""";
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using OfficeOpenXml;
|
||||
|
||||
namespace NBConfigBuilder;
|
||||
|
||||
public sealed class ExcelWorksheets(ExcelExporter excelExporter)
|
||||
{
|
||||
public bool TryGetValue(string worksheetName, out ExcelWorksheet excelWorksheet)
|
||||
{
|
||||
if (excelExporter.Worksheets.TryGetValue(worksheetName, out excelWorksheet))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var computeHash64 = HashCodeHelper.ComputeHash64(worksheetName);
|
||||
if (!excelExporter.VersionInfo.WorksheetNames.Contains(computeHash64))
|
||||
{
|
||||
Log.Info($"{worksheetName} is not exist!");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
17
Tools/ConfigBuilder/NBConfigBuilder/Exporter/ExportInfo.cs
Normal file
17
Tools/ConfigBuilder/NBConfigBuilder/Exporter/ExportInfo.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
namespace NBConfigBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// 导出信息类,用于存储导出操作的名称和文件信息。
|
||||
/// </summary>
|
||||
public class ExportInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 导出操作的名称。
|
||||
/// </summary>
|
||||
public string Name;
|
||||
/// <summary>
|
||||
/// 导出操作生成的文件信息。
|
||||
/// </summary>
|
||||
public FileInfo FileInfo;
|
||||
}
|
||||
24
Tools/ConfigBuilder/NBConfigBuilder/Exporter/ExportType.cs
Normal file
24
Tools/ConfigBuilder/NBConfigBuilder/Exporter/ExportType.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace NBConfigBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// 导出类型枚举,用于标识不同类型的导出操作。
|
||||
/// </summary>
|
||||
public enum ExportType
|
||||
{
|
||||
/// <summary>
|
||||
/// 无导出类型。
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// 所有数据的增量导出Excel类型。
|
||||
/// </summary>
|
||||
AllExcelIncrement = 1,
|
||||
/// <summary>
|
||||
/// 所有数据的全量导出Excel类型。
|
||||
/// </summary>
|
||||
AllExcel = 2,
|
||||
/// <summary>
|
||||
/// 导出类型枚举的最大值,一定要放在最后。
|
||||
/// </summary>
|
||||
Max,
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace NBConfigBuilder;
|
||||
|
||||
public class VersionInfo
|
||||
{
|
||||
public SortedSet<long> WorksheetNames = [];
|
||||
public SortedDictionary<long, long> Tables = new();
|
||||
}
|
||||
265
Tools/ConfigBuilder/NBConfigBuilder/Form1.Designer.cs
generated
Normal file
265
Tools/ConfigBuilder/NBConfigBuilder/Form1.Designer.cs
generated
Normal file
@@ -0,0 +1,265 @@
|
||||
namespace NBConfigBuilder
|
||||
{
|
||||
partial class Form1
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
textBoxExcelPath = new TextBox();
|
||||
buttonSelectExcelPath = new Button();
|
||||
label1 = new Label();
|
||||
textBoxClientGenPath = new TextBox();
|
||||
buttonSelectClientPath = new Button();
|
||||
checkBoxGenClient = new CheckBox();
|
||||
label2 = new Label();
|
||||
textBoxServerGenPath = new TextBox();
|
||||
label3 = new Label();
|
||||
checkBoxGenServer = new CheckBox();
|
||||
buttonSelectServerPath = new Button();
|
||||
buttonRun = new Button();
|
||||
textBoxClientGenJsonPath = new TextBox();
|
||||
label4 = new Label();
|
||||
buttonSelectClientJsonPath = new Button();
|
||||
textBoxServerGenJsonPath = new TextBox();
|
||||
label5 = new Label();
|
||||
buttonSelectServerJsonPath = new Button();
|
||||
SuspendLayout();
|
||||
//
|
||||
// textBoxExcelPath
|
||||
//
|
||||
textBoxExcelPath.Location = new Point(84, 13);
|
||||
textBoxExcelPath.Name = "textBoxExcelPath";
|
||||
textBoxExcelPath.Size = new Size(322, 23);
|
||||
textBoxExcelPath.TabIndex = 0;
|
||||
//
|
||||
// buttonSelectExcelPath
|
||||
//
|
||||
buttonSelectExcelPath.Location = new Point(412, 13);
|
||||
buttonSelectExcelPath.Name = "buttonSelectExcelPath";
|
||||
buttonSelectExcelPath.Size = new Size(75, 23);
|
||||
buttonSelectExcelPath.TabIndex = 1;
|
||||
buttonSelectExcelPath.Text = "选择";
|
||||
buttonSelectExcelPath.UseVisualStyleBackColor = true;
|
||||
buttonSelectExcelPath.Click += buttonSelectExcelPath_Click;
|
||||
//
|
||||
// label1
|
||||
//
|
||||
label1.AutoSize = true;
|
||||
label1.Location = new Point(7, 17);
|
||||
label1.Name = "label1";
|
||||
label1.Size = new Size(68, 17);
|
||||
label1.TabIndex = 2;
|
||||
label1.Text = "配置表路径";
|
||||
//
|
||||
// textBoxClientGenPath
|
||||
//
|
||||
textBoxClientGenPath.Location = new Point(84, 42);
|
||||
textBoxClientGenPath.Name = "textBoxClientGenPath";
|
||||
textBoxClientGenPath.Size = new Size(318, 23);
|
||||
textBoxClientGenPath.TabIndex = 3;
|
||||
//
|
||||
// buttonSelectClientPath
|
||||
//
|
||||
buttonSelectClientPath.Location = new Point(412, 42);
|
||||
buttonSelectClientPath.Name = "buttonSelectClientPath";
|
||||
buttonSelectClientPath.Size = new Size(75, 23);
|
||||
buttonSelectClientPath.TabIndex = 4;
|
||||
buttonSelectClientPath.Text = "选择";
|
||||
buttonSelectClientPath.UseVisualStyleBackColor = true;
|
||||
buttonSelectClientPath.Click += buttonSelectClientPath_Click;
|
||||
//
|
||||
// checkBoxGenClient
|
||||
//
|
||||
checkBoxGenClient.AutoSize = true;
|
||||
checkBoxGenClient.Checked = true;
|
||||
checkBoxGenClient.CheckState = CheckState.Checked;
|
||||
checkBoxGenClient.Location = new Point(495, 44);
|
||||
checkBoxGenClient.Name = "checkBoxGenClient";
|
||||
checkBoxGenClient.Size = new Size(87, 21);
|
||||
checkBoxGenClient.TabIndex = 5;
|
||||
checkBoxGenClient.Text = "生产服务端";
|
||||
checkBoxGenClient.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// label2
|
||||
//
|
||||
label2.AutoSize = true;
|
||||
label2.Location = new Point(7, 47);
|
||||
label2.Name = "label2";
|
||||
label2.Size = new Size(68, 17);
|
||||
label2.TabIndex = 6;
|
||||
label2.Text = "客户端代码";
|
||||
//
|
||||
// textBoxServerGenPath
|
||||
//
|
||||
textBoxServerGenPath.Location = new Point(84, 100);
|
||||
textBoxServerGenPath.Name = "textBoxServerGenPath";
|
||||
textBoxServerGenPath.Size = new Size(318, 23);
|
||||
textBoxServerGenPath.TabIndex = 7;
|
||||
//
|
||||
// label3
|
||||
//
|
||||
label3.AutoSize = true;
|
||||
label3.Location = new Point(7, 103);
|
||||
label3.Name = "label3";
|
||||
label3.Size = new Size(68, 17);
|
||||
label3.TabIndex = 8;
|
||||
label3.Text = "服务端代码";
|
||||
//
|
||||
// checkBoxGenServer
|
||||
//
|
||||
checkBoxGenServer.AutoSize = true;
|
||||
checkBoxGenServer.Checked = true;
|
||||
checkBoxGenServer.CheckState = CheckState.Checked;
|
||||
checkBoxGenServer.Location = new Point(495, 16);
|
||||
checkBoxGenServer.Name = "checkBoxGenServer";
|
||||
checkBoxGenServer.Size = new Size(87, 21);
|
||||
checkBoxGenServer.TabIndex = 9;
|
||||
checkBoxGenServer.Text = "生产客户端";
|
||||
checkBoxGenServer.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// buttonSelectServerPath
|
||||
//
|
||||
buttonSelectServerPath.Location = new Point(412, 100);
|
||||
buttonSelectServerPath.Name = "buttonSelectServerPath";
|
||||
buttonSelectServerPath.Size = new Size(75, 23);
|
||||
buttonSelectServerPath.TabIndex = 10;
|
||||
buttonSelectServerPath.Text = "选择";
|
||||
buttonSelectServerPath.UseVisualStyleBackColor = true;
|
||||
buttonSelectServerPath.Click += buttonSelectServerPath_Click;
|
||||
//
|
||||
// buttonRun
|
||||
//
|
||||
buttonRun.Location = new Point(495, 71);
|
||||
buttonRun.Name = "buttonRun";
|
||||
buttonRun.Size = new Size(80, 81);
|
||||
buttonRun.TabIndex = 12;
|
||||
buttonRun.Text = "执行";
|
||||
buttonRun.UseVisualStyleBackColor = true;
|
||||
buttonRun.Click += buttonRun_Click;
|
||||
//
|
||||
// textBoxClientGenJsonPath
|
||||
//
|
||||
textBoxClientGenJsonPath.Location = new Point(84, 71);
|
||||
textBoxClientGenJsonPath.Name = "textBoxClientGenJsonPath";
|
||||
textBoxClientGenJsonPath.Size = new Size(318, 23);
|
||||
textBoxClientGenJsonPath.TabIndex = 13;
|
||||
//
|
||||
// label4
|
||||
//
|
||||
label4.AutoSize = true;
|
||||
label4.Location = new Point(7, 77);
|
||||
label4.Name = "label4";
|
||||
label4.Size = new Size(68, 17);
|
||||
label4.TabIndex = 14;
|
||||
label4.Text = "客户端配置";
|
||||
//
|
||||
// buttonSelectClientJsonPath
|
||||
//
|
||||
buttonSelectClientJsonPath.Location = new Point(412, 71);
|
||||
buttonSelectClientJsonPath.Name = "buttonSelectClientJsonPath";
|
||||
buttonSelectClientJsonPath.Size = new Size(75, 23);
|
||||
buttonSelectClientJsonPath.TabIndex = 15;
|
||||
buttonSelectClientJsonPath.Text = "选择";
|
||||
buttonSelectClientJsonPath.UseVisualStyleBackColor = true;
|
||||
buttonSelectClientJsonPath.Click += buttonSelectClientJsonPath_Click;
|
||||
//
|
||||
// textBoxServerGenJsonPath
|
||||
//
|
||||
textBoxServerGenJsonPath.Location = new Point(85, 129);
|
||||
textBoxServerGenJsonPath.Name = "textBoxServerGenJsonPath";
|
||||
textBoxServerGenJsonPath.Size = new Size(317, 23);
|
||||
textBoxServerGenJsonPath.TabIndex = 16;
|
||||
//
|
||||
// label5
|
||||
//
|
||||
label5.AutoSize = true;
|
||||
label5.Location = new Point(7, 132);
|
||||
label5.Name = "label5";
|
||||
label5.Size = new Size(68, 17);
|
||||
label5.TabIndex = 17;
|
||||
label5.Text = "服务端配置";
|
||||
//
|
||||
// buttonSelectServerJsonPath
|
||||
//
|
||||
buttonSelectServerJsonPath.Location = new Point(412, 129);
|
||||
buttonSelectServerJsonPath.Name = "buttonSelectServerJsonPath";
|
||||
buttonSelectServerJsonPath.Size = new Size(75, 23);
|
||||
buttonSelectServerJsonPath.TabIndex = 18;
|
||||
buttonSelectServerJsonPath.Text = "选择";
|
||||
buttonSelectServerJsonPath.UseVisualStyleBackColor = true;
|
||||
buttonSelectServerJsonPath.Click += buttonSelectServerJsonPath_Click;
|
||||
//
|
||||
// Form1
|
||||
//
|
||||
AutoScaleDimensions = new SizeF(7F, 17F);
|
||||
AutoScaleMode = AutoScaleMode.Font;
|
||||
ClientSize = new Size(585, 161);
|
||||
Controls.Add(buttonSelectServerJsonPath);
|
||||
Controls.Add(label5);
|
||||
Controls.Add(textBoxServerGenJsonPath);
|
||||
Controls.Add(buttonSelectClientJsonPath);
|
||||
Controls.Add(label4);
|
||||
Controls.Add(textBoxClientGenJsonPath);
|
||||
Controls.Add(buttonRun);
|
||||
Controls.Add(buttonSelectServerPath);
|
||||
Controls.Add(checkBoxGenServer);
|
||||
Controls.Add(label3);
|
||||
Controls.Add(textBoxServerGenPath);
|
||||
Controls.Add(label2);
|
||||
Controls.Add(checkBoxGenClient);
|
||||
Controls.Add(buttonSelectClientPath);
|
||||
Controls.Add(textBoxClientGenPath);
|
||||
Controls.Add(label1);
|
||||
Controls.Add(buttonSelectExcelPath);
|
||||
Controls.Add(textBoxExcelPath);
|
||||
Name = "Form1";
|
||||
Text = "配置生成导出";
|
||||
ResumeLayout(false);
|
||||
PerformLayout();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private TextBox textBoxExcelPath;
|
||||
private Button buttonSelectExcelPath;
|
||||
private Label label1;
|
||||
private TextBox textBoxClientGenPath;
|
||||
private Button buttonSelectClientPath;
|
||||
private CheckBox checkBoxGenClient;
|
||||
private Label label2;
|
||||
private TextBox textBoxServerGenPath;
|
||||
private Label label3;
|
||||
private CheckBox checkBoxGenServer;
|
||||
private Button buttonSelectServerPath;
|
||||
private Button buttonRun;
|
||||
private TextBox textBoxClientGenJsonPath;
|
||||
private Label label4;
|
||||
private Button buttonSelectClientJsonPath;
|
||||
private TextBox textBoxServerGenJsonPath;
|
||||
private Label label5;
|
||||
private Button buttonSelectServerJsonPath;
|
||||
}
|
||||
}
|
||||
141
Tools/ConfigBuilder/NBConfigBuilder/Form1.cs
Normal file
141
Tools/ConfigBuilder/NBConfigBuilder/Form1.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace NBConfigBuilder
|
||||
{
|
||||
public partial class Form1 : Form
|
||||
{
|
||||
// 配置文件路径
|
||||
private readonly string configPath =
|
||||
Path.Combine(Path.GetDirectoryName(Application.ExecutablePath) ?? string.Empty, "config.json");
|
||||
|
||||
public Form1()
|
||||
{
|
||||
InitializeComponent();
|
||||
// 设置窗口大小不可变
|
||||
this.FormBorderStyle = FormBorderStyle.FixedSingle;
|
||||
this.MaximizeBox = false;
|
||||
|
||||
// 加载保存的配置
|
||||
LoadConfig();
|
||||
}
|
||||
|
||||
private void buttonRun_Click(object sender, EventArgs e)
|
||||
{
|
||||
// 保存当前配置
|
||||
SaveConfig();
|
||||
new ExcelExporter(ExportType.AllExcel).Run();
|
||||
}
|
||||
|
||||
private void buttonSelectExcelPath_Click(object sender, EventArgs e)
|
||||
{
|
||||
using (FolderBrowserDialog folderDlg = new FolderBrowserDialog())
|
||||
{
|
||||
folderDlg.Description = "请选择配置表路径";
|
||||
if (folderDlg.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
textBoxExcelPath.Text = folderDlg.SelectedPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void buttonSelectClientJsonPath_Click(object sender, EventArgs e)
|
||||
{
|
||||
using (FolderBrowserDialog folderDlg = new FolderBrowserDialog())
|
||||
{
|
||||
folderDlg.Description = @"选择客户端json保存目录";
|
||||
if (folderDlg.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
textBoxClientGenJsonPath.Text = folderDlg.SelectedPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void buttonSelectServerJsonPath_Click(object sender, EventArgs e)
|
||||
{
|
||||
using (FolderBrowserDialog folderDlg = new FolderBrowserDialog())
|
||||
{
|
||||
folderDlg.Description = @"选择服务端json保存目录";
|
||||
if (folderDlg.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
textBoxServerGenJsonPath.Text = folderDlg.SelectedPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void buttonSelectClientPath_Click(object sender, EventArgs e)
|
||||
{
|
||||
using (FolderBrowserDialog folderDlg = new FolderBrowserDialog())
|
||||
{
|
||||
folderDlg.Description = @"选择客户端代码保存目录";
|
||||
if (folderDlg.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
textBoxClientGenPath.Text = folderDlg.SelectedPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void buttonSelectServerPath_Click(object sender, EventArgs e)
|
||||
{
|
||||
using (FolderBrowserDialog folderDlg = new FolderBrowserDialog())
|
||||
{
|
||||
folderDlg.Description = @"选择服务端代码保存目录";
|
||||
if (folderDlg.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
textBoxServerGenPath.Text = folderDlg.SelectedPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加保存和加载配置的方法
|
||||
private void SaveConfig()
|
||||
{
|
||||
var config = new AppConfig
|
||||
{
|
||||
ExcelPath = textBoxExcelPath.Text,
|
||||
ClientPath = textBoxClientGenPath.Text,
|
||||
ClientJsonPath = textBoxClientGenJsonPath.Text,
|
||||
ServerPath = textBoxServerGenPath.Text,
|
||||
ServerJsonPath = textBoxServerGenJsonPath.Text,
|
||||
GenClient = checkBoxGenClient.Checked,
|
||||
GenServer = checkBoxGenServer.Checked,
|
||||
};
|
||||
App.Config = config;
|
||||
try
|
||||
{
|
||||
string json = JsonSerializer.Serialize(config, new JsonSerializerOptions { WriteIndented = true });
|
||||
File.WriteAllText(configPath, json);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"保存配置失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadConfig()
|
||||
{
|
||||
if (!File.Exists(configPath)) return;
|
||||
|
||||
try
|
||||
{
|
||||
string json = File.ReadAllText(configPath);
|
||||
var config = JsonSerializer.Deserialize<AppConfig>(json);
|
||||
|
||||
if (config != null)
|
||||
{
|
||||
App.Config = config;
|
||||
textBoxExcelPath.Text = config.ExcelPath ?? "";
|
||||
textBoxClientGenPath.Text = config.ClientPath ?? "";
|
||||
textBoxServerGenPath.Text = config.ServerPath ?? "";
|
||||
checkBoxGenClient.Checked = config.GenClient;
|
||||
checkBoxGenServer.Checked = config.GenServer;
|
||||
textBoxClientGenJsonPath.Text = config.ClientJsonPath ?? "";
|
||||
textBoxServerGenJsonPath.Text = config.ServerJsonPath ?? "";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"加载配置失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
120
Tools/ConfigBuilder/NBConfigBuilder/Form1.resx
Normal file
120
Tools/ConfigBuilder/NBConfigBuilder/Form1.resx
Normal file
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
97
Tools/ConfigBuilder/NBConfigBuilder/Log.cs
Normal file
97
Tools/ConfigBuilder/NBConfigBuilder/Log.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace NBConfigBuilder
|
||||
{
|
||||
public static class Log
|
||||
{
|
||||
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
static Log()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 记录信息级别的日志消息。
|
||||
/// </summary>
|
||||
/// <param name="msg">日志消息。</param>
|
||||
public static void Info(string msg)
|
||||
{
|
||||
logger.Info(msg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 记录错误级别的日志消息,并附带调用栈信息。
|
||||
/// </summary>
|
||||
/// <param name="msg">日志消息。</param>
|
||||
public static void Error(string msg)
|
||||
{
|
||||
var st = new StackTrace(1, true);
|
||||
logger.Error($"{msg}\n{st}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 记录异常的错误级别的日志消息,并附带调用栈信息。
|
||||
/// </summary>
|
||||
/// <param name="e">异常对象。</param>
|
||||
public static void Error(Exception e)
|
||||
{
|
||||
if (e.Data.Contains("StackTrace"))
|
||||
{
|
||||
logger.Error($"{e.Data["StackTrace"]}\n{e}");
|
||||
return;
|
||||
}
|
||||
|
||||
var str = e.ToString();
|
||||
logger.Error(str);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 记录信息级别的格式化日志消息。
|
||||
/// </summary>
|
||||
/// <param name="message">日志消息模板。</param>
|
||||
/// <param name="args">格式化参数。</param>
|
||||
public static void Info(string message, params object[] args)
|
||||
{
|
||||
logger.Info(message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 记录调试级别的日志消息。
|
||||
/// </summary>
|
||||
/// <param name="msg">日志消息。</param>
|
||||
public static void Debug(string msg)
|
||||
{
|
||||
logger.Debug(msg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 记录警告级别的日志消息。
|
||||
/// </summary>
|
||||
/// <param name="msg">日志消息。</param>
|
||||
public static void Warn(string msg)
|
||||
{
|
||||
logger.Warn(msg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 记录严重错误级别的日志消息。
|
||||
/// </summary>
|
||||
/// <param name="msg">日志消息。</param>
|
||||
public static void Fatal(string msg)
|
||||
{
|
||||
logger.Fatal(msg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 记录异常的错误级别的日志消息。
|
||||
/// </summary>
|
||||
/// <param name="e">异常对象。</param>
|
||||
/// <param name="message">附加消息。</param>
|
||||
public static void Error(Exception e, string message)
|
||||
{
|
||||
logger.Error(e, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Tools/ConfigBuilder/NBConfigBuilder/NBConfigBuilder.csproj
Normal file
25
Tools/ConfigBuilder/NBConfigBuilder/NBConfigBuilder.csproj
Normal file
@@ -0,0 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<Nullable>disable</Nullable>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EPPlus" Version="8.2.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
<PackageReference Include="NLog" Version="6.0.4" />
|
||||
<PackageReference Include="protobuf-net" Version="3.2.56" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="NLog.config">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Compile Update="Form1.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
25
Tools/ConfigBuilder/NBConfigBuilder/NLog.config
Normal file
25
Tools/ConfigBuilder/NBConfigBuilder/NLog.config
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
|
||||
<targets>
|
||||
<!-- 文件目标 -->
|
||||
<target xsi:type="File"
|
||||
name="fileTarget"
|
||||
fileName="logs/${shortdate}.log"
|
||||
layout="${longdate} ${uppercase:${level}} ${message} ${exception:format=ToString}" />
|
||||
|
||||
<!-- 控制台目标 -->
|
||||
<target xsi:type="Console"
|
||||
name="consoleTarget"
|
||||
layout="${longdate} ${uppercase:${level}} ${message} ${exception:format=ToString}" />
|
||||
</targets>
|
||||
|
||||
<rules>
|
||||
<!-- 记录所有级别高于 Debug 的日志到文件 -->
|
||||
<logger name="*" minlevel="Debug" writeTo="fileTarget" />
|
||||
|
||||
<!-- 记录所有级别高于 Info 的日志到控制台 -->
|
||||
<logger name="*" minlevel="Info" writeTo="consoleTarget" />
|
||||
</rules>
|
||||
</nlog>
|
||||
25
Tools/ConfigBuilder/NBConfigBuilder/Program.cs
Normal file
25
Tools/ConfigBuilder/NBConfigBuilder/Program.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using NLog;
|
||||
|
||||
namespace NBConfigBuilder
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
// 配置NLog
|
||||
var logger = LogManager.GetCurrentClassLogger();
|
||||
logger.Info("Application started");
|
||||
|
||||
// To customize application configuration such as set high DPI settings or default font,
|
||||
// see https://aka.ms/applicationconfiguration.
|
||||
ApplicationConfiguration.Initialize();
|
||||
Application.Run(new Form1());
|
||||
|
||||
logger.Info("Application closing");
|
||||
}
|
||||
}
|
||||
}
|
||||
182
Tools/ConfigBuilder/NBConfigBuilder/Utils/FileHelper.cs
Normal file
182
Tools/ConfigBuilder/NBConfigBuilder/Utils/FileHelper.cs
Normal file
@@ -0,0 +1,182 @@
|
||||
using System.Text;
|
||||
|
||||
namespace NBConfigBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// 文件操作助手类,提供了各种文件操作方法。
|
||||
/// </summary>
|
||||
public static partial class FileHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取相对路径的完整路径。
|
||||
/// </summary>
|
||||
/// <param name="relativePath">相对路径。</param>
|
||||
/// <returns>完整路径。</returns>
|
||||
public static string GetFullPath(string relativePath)
|
||||
{
|
||||
return Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), relativePath));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取相对路径的完整路径。
|
||||
/// </summary>
|
||||
/// <param name="relativePath">相对于指定的目录的相对路径。</param>
|
||||
/// <param name="srcDir">指定的目录</param>
|
||||
/// <returns>完整路径。</returns>
|
||||
public static string GetFullPath(string relativePath, string srcDir)
|
||||
{
|
||||
return Path.GetFullPath(Path.Combine(srcDir, relativePath));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取相对路径的的文本信息。
|
||||
/// </summary>
|
||||
/// <param name="relativePath"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<string> GetTextByRelativePath(string relativePath)
|
||||
{
|
||||
var fullPath = GetFullPath(relativePath);
|
||||
return await File.ReadAllTextAsync(fullPath, Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取绝对路径的的文本信息。
|
||||
/// </summary>
|
||||
/// <param name="fullPath"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<string> GetText(string fullPath)
|
||||
{
|
||||
return await File.ReadAllTextAsync(fullPath, Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据文件夹路径创建文件夹,如果文件夹不存在会自动创建文件夹。
|
||||
/// </summary>
|
||||
/// <param name="directoryPath"></param>
|
||||
public static void CreateDirectory(string directoryPath)
|
||||
{
|
||||
if (directoryPath.LastIndexOf('/') != directoryPath.Length - 1)
|
||||
{
|
||||
directoryPath += "/";
|
||||
}
|
||||
|
||||
var directoriesByFilePath = GetDirectoriesByFilePath(directoryPath);
|
||||
|
||||
foreach (var dir in directoriesByFilePath)
|
||||
{
|
||||
if (Directory.Exists(dir))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将文件复制到目标路径,如果目标目录不存在会自动创建目录。
|
||||
/// </summary>
|
||||
/// <param name="sourceFile">源文件路径。</param>
|
||||
/// <param name="destinationFile">目标文件路径。</param>
|
||||
/// <param name="overwrite">是否覆盖已存在的目标文件。</param>
|
||||
public static void Copy(string sourceFile, string destinationFile, bool overwrite)
|
||||
{
|
||||
CreateDirectory(destinationFile);
|
||||
File.Copy(sourceFile, destinationFile, overwrite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取文件路径内的所有文件夹路径。
|
||||
/// </summary>
|
||||
/// <param name="filePath">文件路径。</param>
|
||||
/// <returns>文件夹路径列表。</returns>
|
||||
public static IEnumerable<string> GetDirectoriesByFilePath(string filePath)
|
||||
{
|
||||
var dir = "";
|
||||
var fileDirectories = filePath.Split('/');
|
||||
|
||||
for (var i = 0; i < fileDirectories.Length - 1; i++)
|
||||
{
|
||||
dir = $"{dir}{fileDirectories[i]}/";
|
||||
yield return dir;
|
||||
}
|
||||
|
||||
if (fileDirectories.Length == 1)
|
||||
{
|
||||
yield return filePath;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将文件夹内的所有内容复制到目标位置。
|
||||
/// </summary>
|
||||
/// <param name="sourceDirectory">源文件夹路径。</param>
|
||||
/// <param name="destinationDirectory">目标文件夹路径。</param>
|
||||
/// <param name="overwrite">是否覆盖已存在的文件。</param>
|
||||
public static void CopyDirectory(string sourceDirectory, string destinationDirectory, bool overwrite)
|
||||
{
|
||||
// 创建目标文件夹
|
||||
|
||||
if (!Directory.Exists(destinationDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(destinationDirectory);
|
||||
}
|
||||
|
||||
// 获取当前文件夹中的所有文件
|
||||
|
||||
var files = Directory.GetFiles(sourceDirectory);
|
||||
|
||||
// 拷贝文件到目标文件夹
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
var fileName = Path.GetFileName(file);
|
||||
var destinationPath = Path.Combine(destinationDirectory, fileName);
|
||||
File.Copy(file, destinationPath, overwrite);
|
||||
}
|
||||
|
||||
// 获取源文件夹中的所有子文件夹
|
||||
|
||||
var directories = Directory.GetDirectories(sourceDirectory);
|
||||
|
||||
// 递归方式拷贝文件夹
|
||||
|
||||
foreach (var directory in directories)
|
||||
{
|
||||
var directoryName = Path.GetFileName(directory);
|
||||
var destinationPath = Path.Combine(destinationDirectory, directoryName);
|
||||
CopyDirectory(directory, destinationPath, overwrite);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取目录下的所有文件
|
||||
/// </summary>
|
||||
/// <param name="folderPath">文件夹路径。</param>
|
||||
/// <param name="searchPattern">需要查找的文件通配符</param>
|
||||
/// <param name="searchOption">查找的类型</param>
|
||||
/// <returns></returns>
|
||||
public static string[] GetDirectoryFile(string folderPath, string searchPattern, SearchOption searchOption)
|
||||
{
|
||||
return Directory.GetFiles(folderPath, searchPattern, searchOption);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空文件夹内的所有文件。
|
||||
/// </summary>
|
||||
/// <param name="folderPath">文件夹路径。</param>
|
||||
public static void ClearDirectoryFile(string folderPath)
|
||||
{
|
||||
if (!Directory.Exists(folderPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var files = Directory.GetFiles(folderPath);
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
228
Tools/ConfigBuilder/NBConfigBuilder/Utils/HashCodeHelper.cs
Normal file
228
Tools/ConfigBuilder/NBConfigBuilder/Utils/HashCodeHelper.cs
Normal file
@@ -0,0 +1,228 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace NBConfigBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// HashCode算法帮助类
|
||||
/// </summary>
|
||||
public static partial class HashCodeHelper
|
||||
{
|
||||
private static readonly SHA256 Sha256Hash = SHA256.Create();
|
||||
|
||||
/// <summary>
|
||||
/// 计算两个字符串的HashCode
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
public static int GetHashCode(string a, string b)
|
||||
{
|
||||
var hash = 17;
|
||||
hash = hash * 31 + a.GetHashCode();
|
||||
hash = hash * 31 + b.GetHashCode();
|
||||
return hash;
|
||||
}
|
||||
#if FANTASY_NET || !FANTASY_WEBGL
|
||||
/// <summary>
|
||||
/// 使用bkdr算法生成一个long的值
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static unsafe long GetBKDRHashCode(string str)
|
||||
{
|
||||
ulong hash = 0;
|
||||
// 如果要修改这个种子、建议选择一个质数来做种子
|
||||
const uint seed = 13131; // 31 131 1313 13131 131313 etc..
|
||||
fixed (char* p = str)
|
||||
{
|
||||
for (var i = 0; i < str.Length; i++)
|
||||
{
|
||||
var c = p[i];
|
||||
var high = (byte)(c >> 8);
|
||||
var low = (byte)(c & byte.MaxValue);
|
||||
hash = hash * seed + high;
|
||||
hash = hash * seed + low;
|
||||
}
|
||||
}
|
||||
|
||||
return (long)hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用MurmurHash3算法生成一个uint的值
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static unsafe uint MurmurHash3(string str)
|
||||
{
|
||||
const uint seed = 0xc58f1a7b;
|
||||
uint hash = seed;
|
||||
uint c1 = 0xcc9e2d51;
|
||||
uint c2 = 0x1b873593;
|
||||
|
||||
fixed (char* p = str)
|
||||
{
|
||||
var current = p;
|
||||
|
||||
for (var i = 0; i < str.Length; i++)
|
||||
{
|
||||
var k1 = (uint)(*current);
|
||||
k1 *= c1;
|
||||
k1 = (k1 << 15) | (k1 >> (32 - 15));
|
||||
k1 *= c2;
|
||||
|
||||
hash ^= k1;
|
||||
hash = (hash << 13) | (hash >> (32 - 13));
|
||||
hash = hash * 5 + 0xe6546b64;
|
||||
|
||||
current++;
|
||||
}
|
||||
}
|
||||
|
||||
hash ^= (uint)str.Length;
|
||||
hash ^= hash >> 16;
|
||||
hash *= 0x85ebca6b;
|
||||
hash ^= hash >> 13;
|
||||
hash *= 0xc2b2ae35;
|
||||
hash ^= hash >> 16;
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用MurmurHash3算法生成一个long的值
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static unsafe long ComputeHash64(string str)
|
||||
{
|
||||
const ulong seed = 0xc58f1a7bc58f1a7bUL; // 64-bit seed
|
||||
var hash = seed;
|
||||
var c1 = 0x87c37b91114253d5UL;
|
||||
var c2 = 0x4cf5ad432745937fUL;
|
||||
|
||||
fixed (char* p = str)
|
||||
{
|
||||
var current = p;
|
||||
|
||||
for (var i = 0; i < str.Length; i++)
|
||||
{
|
||||
var k1 = (ulong)(*current);
|
||||
k1 *= c1;
|
||||
k1 = (k1 << 31) | (k1 >> (64 - 31));
|
||||
k1 *= c2;
|
||||
|
||||
hash ^= k1;
|
||||
hash = (hash << 27) | (hash >> (64 - 27));
|
||||
hash = hash * 5 + 0x52dce729;
|
||||
|
||||
current++;
|
||||
}
|
||||
}
|
||||
|
||||
hash ^= (ulong)str.Length;
|
||||
hash ^= hash >> 33;
|
||||
hash *= 0xff51afd7ed558ccdUL;
|
||||
hash ^= hash >> 33;
|
||||
hash *= 0xc4ceb9fe1a85ec53UL;
|
||||
hash ^= hash >> 33;
|
||||
return (long)hash;
|
||||
}
|
||||
#endif
|
||||
#if FANTASY_WEBGL
|
||||
/// <summary>
|
||||
/// 使用bkdr算法生成一个long的值
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static long GetBKDRHashCode(string str)
|
||||
{
|
||||
long hash = 0;
|
||||
// 如果要修改这个种子、建议选择一个质数来做种子
|
||||
const uint seed = 13131; // 31 131 1313 13131 131313 etc..
|
||||
foreach (var c in str)
|
||||
{
|
||||
var high = (byte)(c >> 8);
|
||||
var low = (byte)(c & byte.MaxValue);
|
||||
hash = hash * seed + high;
|
||||
hash = hash * seed + low;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
/// <summary>
|
||||
/// 使用MurmurHash3算法生成一个uint的值
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static uint MurmurHash3(string str)
|
||||
{
|
||||
const uint seed = 0xc58f1a7b;
|
||||
uint hash = seed;
|
||||
uint c1 = 0xcc9e2d51;
|
||||
uint c2 = 0x1b873593;
|
||||
|
||||
foreach (var t in str)
|
||||
{
|
||||
var k1 = (uint)(t);
|
||||
k1 *= c1;
|
||||
k1 = (k1 << 15) | (k1 >> (32 - 15));
|
||||
k1 *= c2;
|
||||
|
||||
hash ^= k1;
|
||||
hash = (hash << 13) | (hash >> (32 - 13));
|
||||
hash = hash * 5 + 0xe6546b64;
|
||||
}
|
||||
|
||||
hash ^= (uint)str.Length;
|
||||
hash ^= hash >> 16;
|
||||
hash *= 0x85ebca6b;
|
||||
hash ^= hash >> 13;
|
||||
hash *= 0xc2b2ae35;
|
||||
hash ^= hash >> 16;
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用MurmurHash3算法生成一个long的值
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static long ComputeHash64(string str)
|
||||
{
|
||||
const ulong seed = 0xc58f1a7bc58f1a7bUL; // 64-bit seed
|
||||
var hash = seed;
|
||||
var c1 = 0x87c37b91114253d5UL;
|
||||
var c2 = 0x4cf5ad432745937fUL;
|
||||
|
||||
foreach (var t in str)
|
||||
{
|
||||
var k1 = (ulong)(t);
|
||||
k1 *= c1;
|
||||
k1 = (k1 << 31) | (k1 >> (64 - 31));
|
||||
k1 *= c2;
|
||||
|
||||
hash ^= k1;
|
||||
hash = (hash << 27) | (hash >> (64 - 27));
|
||||
hash = hash * 5 + 0x52dce729;
|
||||
}
|
||||
|
||||
hash ^= (ulong)str.Length;
|
||||
hash ^= hash >> 33;
|
||||
hash *= 0xff51afd7ed558ccdUL;
|
||||
hash ^= hash >> 33;
|
||||
hash *= 0xc4ceb9fe1a85ec53UL;
|
||||
hash ^= hash >> 33;
|
||||
return (long)hash;
|
||||
}
|
||||
#endif
|
||||
/// <summary>
|
||||
/// 根据字符串计算一个Hash值
|
||||
/// </summary>
|
||||
/// <param name="rawData"></param>
|
||||
/// <returns></returns>
|
||||
public static int ComputeSha256HashAsInt(string rawData)
|
||||
{
|
||||
var bytes = Sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(rawData));
|
||||
return (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3];
|
||||
}
|
||||
}
|
||||
65
Tools/ConfigBuilder/NBConfigBuilder/Utils/TimeHelper.cs
Normal file
65
Tools/ConfigBuilder/NBConfigBuilder/Utils/TimeHelper.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
namespace NBConfigBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// 提供与时间相关的帮助方法。
|
||||
/// </summary>
|
||||
public static partial class TimeHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 一小时的毫秒值。
|
||||
/// </summary>
|
||||
public const long Hour = 3600000;
|
||||
|
||||
/// <summary>
|
||||
/// 一分钟的毫秒值。
|
||||
/// </summary>
|
||||
public const long Minute = 60000;
|
||||
|
||||
/// <summary>
|
||||
/// 一天的毫秒值。
|
||||
/// </summary>
|
||||
public const long OneDay = 86400000;
|
||||
|
||||
// 1970年1月1日的Ticks
|
||||
private const long Epoch = 621355968000000000L;
|
||||
/// <summary>
|
||||
/// 获取当前时间的毫秒数,从1970年1月1日开始计算。
|
||||
/// </summary>
|
||||
public static long Now => (DateTime.UtcNow.Ticks - Epoch) / 10000;
|
||||
|
||||
/// <summary>
|
||||
/// 根据时间获取时间戳
|
||||
/// </summary>
|
||||
public static long Transition(DateTime dateTime)
|
||||
{
|
||||
return (dateTime.ToUniversalTime().Ticks - Epoch) / 10000;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据时间获取 时间戳
|
||||
/// </summary>
|
||||
public static long TransitionToSeconds(DateTime dateTime)
|
||||
{
|
||||
return (dateTime.ToUniversalTime().Ticks - Epoch) / 10000000;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将毫秒数转换为日期时间。
|
||||
/// </summary>
|
||||
/// <param name="timeStamp">要转换的毫秒数。</param>
|
||||
/// <returns>转换后的日期时间。</returns>
|
||||
public static DateTime Transition(this long timeStamp)
|
||||
{
|
||||
return new DateTime(Epoch + timeStamp * 10000, DateTimeKind.Utc).ToUniversalTime();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将毫秒数转换为本地时间的日期时间。
|
||||
/// </summary>
|
||||
/// <param name="timeStamp">要转换的毫秒数。</param>
|
||||
/// <returns>转换后的本地时间的日期时间。</returns>
|
||||
public static DateTime TransitionLocal(this long timeStamp)
|
||||
{
|
||||
return new DateTime(Epoch + timeStamp * 10000, DateTimeKind.Utc).ToLocalTime();
|
||||
}
|
||||
}
|
||||
@@ -664,6 +664,7 @@ public sealed class ExcelExporter
|
||||
|
||||
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)
|
||||
@@ -718,6 +719,7 @@ public sealed class ExcelExporter
|
||||
dynamicInfo.Json.AppendLine($"{json},");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从 Excel 文件加载工作表并返回 ExcelWorksheet 对象。
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user