From 1e7fb10db2bb2b5a38c3f4a59cc4dd679deae973 Mon Sep 17 00:00:00 2001 From: walon Date: Mon, 11 Oct 2021 23:06:41 +0800 Subject: [PATCH] =?UTF-8?q?LubanAssistant=E6=B7=BB=E5=8A=A0=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=AE=9A=E4=B9=89=E5=8A=A0=E8=BD=BD=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/LubanAssistant/AssistantTab.Designer.cs | 152 ++ src/LubanAssistant/AssistantTab.cs | 82 + src/LubanAssistant/AssistantTab.resx | 123 ++ src/LubanAssistant/LubanAssistant.csproj | 413 +++- .../Properties/Settings.Designer.cs | 22 +- .../Properties/Settings.settings | 14 +- src/LubanAssistant/Source/AtomicLong.cs | 36 + src/LubanAssistant/Source/CacheFileUtil.cs | 56 + .../Source/Collections/CollectionExtension.cs | 260 +++ .../Source/Collections/CollectionUtil.cs | 76 + src/LubanAssistant/Source/Common/HashUtil.cs | 91 + src/LubanAssistant/Source/Common/MathUtil.cs | 106 ++ .../Source/Common/SerializationUtil.cs | 214 +++ .../Source/Common/StringUtil.cs | 52 + src/LubanAssistant/Source/Common/TimeUtil.cs | 156 ++ src/LubanAssistant/Source/Common/ValueUtil.cs | 22 + .../Source/DataSources/DataSourceFactory.cs | 43 + src/LubanAssistant/Source/Datas/DText.cs | 54 + src/LubanAssistant/Source/Datas/DType.cs | 28 + .../Source/Defs/CfgDefLoader.cs | 471 +++++ src/LubanAssistant/Source/Defs/DefAssembly.cs | 290 +++ src/LubanAssistant/Source/Defs/DefField.cs | 181 ++ src/LubanAssistant/Source/Defs/DefTypeBase.cs | 69 + src/LubanAssistant/Source/FileInfo.cs | 28 + .../Source/GetImportFileOrDirectory.cs | 63 + src/LubanAssistant/Source/LocalAgent.cs | 122 ++ src/LubanAssistant/Source/QueryFilesExists.cs | 58 + .../Source/Serialization/BeanBase.cs | 11 + .../Source/Serialization/ByteBuf.cs | 1687 +++++++++++++++++ .../Source/Serialization/EUnmarshalError.cs | 10 + .../Source/Serialization/FieldTag.cs | 42 + .../Source/Serialization/ISerializable.cs | 13 + .../Source/Serialization/ITypeId.cs | 7 + .../Serialization/SerializationException.cs | 24 + .../Source/Utils/DataLoaderUtil.cs | 223 +++ src/LubanAssistant/ThisAddIn.cs | 14 +- src/LubanAssistant/app.config | 27 + src/LubanAssistant/packages.config | 20 + 38 files changed, 5347 insertions(+), 13 deletions(-) create mode 100644 src/LubanAssistant/AssistantTab.Designer.cs create mode 100644 src/LubanAssistant/AssistantTab.cs create mode 100644 src/LubanAssistant/AssistantTab.resx create mode 100644 src/LubanAssistant/Source/AtomicLong.cs create mode 100644 src/LubanAssistant/Source/CacheFileUtil.cs create mode 100644 src/LubanAssistant/Source/Collections/CollectionExtension.cs create mode 100644 src/LubanAssistant/Source/Collections/CollectionUtil.cs create mode 100644 src/LubanAssistant/Source/Common/HashUtil.cs create mode 100644 src/LubanAssistant/Source/Common/MathUtil.cs create mode 100644 src/LubanAssistant/Source/Common/SerializationUtil.cs create mode 100644 src/LubanAssistant/Source/Common/StringUtil.cs create mode 100644 src/LubanAssistant/Source/Common/TimeUtil.cs create mode 100644 src/LubanAssistant/Source/Common/ValueUtil.cs create mode 100644 src/LubanAssistant/Source/DataSources/DataSourceFactory.cs create mode 100644 src/LubanAssistant/Source/Datas/DText.cs create mode 100644 src/LubanAssistant/Source/Datas/DType.cs create mode 100644 src/LubanAssistant/Source/Defs/CfgDefLoader.cs create mode 100644 src/LubanAssistant/Source/Defs/DefAssembly.cs create mode 100644 src/LubanAssistant/Source/Defs/DefField.cs create mode 100644 src/LubanAssistant/Source/Defs/DefTypeBase.cs create mode 100644 src/LubanAssistant/Source/FileInfo.cs create mode 100644 src/LubanAssistant/Source/GetImportFileOrDirectory.cs create mode 100644 src/LubanAssistant/Source/LocalAgent.cs create mode 100644 src/LubanAssistant/Source/QueryFilesExists.cs create mode 100644 src/LubanAssistant/Source/Serialization/BeanBase.cs create mode 100644 src/LubanAssistant/Source/Serialization/ByteBuf.cs create mode 100644 src/LubanAssistant/Source/Serialization/EUnmarshalError.cs create mode 100644 src/LubanAssistant/Source/Serialization/FieldTag.cs create mode 100644 src/LubanAssistant/Source/Serialization/ISerializable.cs create mode 100644 src/LubanAssistant/Source/Serialization/ITypeId.cs create mode 100644 src/LubanAssistant/Source/Serialization/SerializationException.cs create mode 100644 src/LubanAssistant/Source/Utils/DataLoaderUtil.cs create mode 100644 src/LubanAssistant/app.config create mode 100644 src/LubanAssistant/packages.config diff --git a/src/LubanAssistant/AssistantTab.Designer.cs b/src/LubanAssistant/AssistantTab.Designer.cs new file mode 100644 index 0000000..9f5542b --- /dev/null +++ b/src/LubanAssistant/AssistantTab.Designer.cs @@ -0,0 +1,152 @@ + +namespace LubanAssistant +{ + partial class AssistantTab : Microsoft.Office.Tools.Ribbon.RibbonBase + { + /// + /// 必需的设计器变量。 + /// + private System.ComponentModel.IContainer components = null; + + public AssistantTab() + : base(Globals.Factory.GetRibbonFactory()) + { + InitializeComponent(); + } + + /// + /// 清理所有正在使用的资源。 + /// + /// 如果应释放托管资源,为 true;否则为 false。 + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region 组件设计器生成的代码 + + /// + /// 设计器支持所需的方法 - 不要修改 + /// 使用代码编辑器修改此方法的内容。 + /// + private void InitializeComponent() + { + this.tab1 = this.Factory.CreateRibbonTab(); + this.group3 = this.Factory.CreateRibbonGroup(); + this.SetRootFile = this.Factory.CreateRibbonButton(); + this.group1 = this.Factory.CreateRibbonGroup(); + this.load = this.Factory.CreateRibbonButton(); + this.group2 = this.Factory.CreateRibbonGroup(); + this.saveAll = this.Factory.CreateRibbonButton(); + this.saveSelected = this.Factory.CreateRibbonButton(); + this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog(); + this.tab1.SuspendLayout(); + this.group3.SuspendLayout(); + this.group1.SuspendLayout(); + this.group2.SuspendLayout(); + this.SuspendLayout(); + // + // tab1 + // + this.tab1.ControlId.ControlIdType = Microsoft.Office.Tools.Ribbon.RibbonControlIdType.Office; + this.tab1.Groups.Add(this.group3); + this.tab1.Groups.Add(this.group1); + this.tab1.Groups.Add(this.group2); + this.tab1.Label = "TabAddIns"; + this.tab1.Name = "tab1"; + // + // group3 + // + this.group3.Items.Add(this.SetRootFile); + this.group3.Name = "group3"; + // + // SetRootFile + // + this.SetRootFile.ControlSize = Microsoft.Office.Core.RibbonControlSize.RibbonControlSizeLarge; + this.SetRootFile.Label = "设置Root文件"; + this.SetRootFile.Name = "SetRootFile"; + this.SetRootFile.ShowImage = true; + this.SetRootFile.Click += new Microsoft.Office.Tools.Ribbon.RibbonControlEventHandler(this.BtnChooseRootFileClick); + // + // group1 + // + this.group1.Items.Add(this.load); + this.group1.Name = "group1"; + // + // load + // + this.load.ControlSize = Microsoft.Office.Core.RibbonControlSize.RibbonControlSizeLarge; + this.load.Label = "加载数据表"; + this.load.Name = "load"; + this.load.ShowImage = true; + this.load.Click += new Microsoft.Office.Tools.Ribbon.RibbonControlEventHandler(this.BtnLoadClick); + // + // group2 + // + this.group2.Items.Add(this.saveAll); + this.group2.Items.Add(this.saveSelected); + this.group2.Name = "group2"; + // + // saveAll + // + this.saveAll.ControlSize = Microsoft.Office.Core.RibbonControlSize.RibbonControlSizeLarge; + this.saveAll.Label = "保存所有"; + this.saveAll.Name = "saveAll"; + this.saveAll.ShowImage = true; + this.saveAll.Click += new Microsoft.Office.Tools.Ribbon.RibbonControlEventHandler(this.BtnSaveAllClick); + // + // saveSelected + // + this.saveSelected.ControlSize = Microsoft.Office.Core.RibbonControlSize.RibbonControlSizeLarge; + this.saveSelected.Label = "保存选中"; + this.saveSelected.Name = "saveSelected"; + this.saveSelected.ShowImage = true; + this.saveSelected.Click += new Microsoft.Office.Tools.Ribbon.RibbonControlEventHandler(this.BtnSaveSelectedClick); + // + // openFileDialog1 + // + this.openFileDialog1.FileName = "openFileDialog1"; + // + // AssistantTab + // + this.Name = "AssistantTab"; + this.RibbonType = "Microsoft.Excel.Workbook"; + this.Tabs.Add(this.tab1); + this.Load += new Microsoft.Office.Tools.Ribbon.RibbonUIEventHandler(this.AssistantTab_Load); + this.tab1.ResumeLayout(false); + this.tab1.PerformLayout(); + this.group3.ResumeLayout(false); + this.group3.PerformLayout(); + this.group1.ResumeLayout(false); + this.group1.PerformLayout(); + this.group2.ResumeLayout(false); + this.group2.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + internal Microsoft.Office.Tools.Ribbon.RibbonTab tab1; + internal Microsoft.Office.Tools.Ribbon.RibbonGroup group1; + internal Microsoft.Office.Tools.Ribbon.RibbonButton load; + internal Microsoft.Office.Tools.Ribbon.RibbonGroup group2; + internal Microsoft.Office.Tools.Ribbon.RibbonButton saveAll; + internal Microsoft.Office.Tools.Ribbon.RibbonGroup group3; + internal Microsoft.Office.Tools.Ribbon.RibbonButton SetRootFile; + internal Microsoft.Office.Tools.Ribbon.RibbonButton saveSelected; + private System.Windows.Forms.OpenFileDialog openFileDialog1; + } + + partial class ThisRibbonCollection + { + internal AssistantTab AssistantTab + { + get { return this.GetRibbon(); } + } + } +} diff --git a/src/LubanAssistant/AssistantTab.cs b/src/LubanAssistant/AssistantTab.cs new file mode 100644 index 0000000..74b00f8 --- /dev/null +++ b/src/LubanAssistant/AssistantTab.cs @@ -0,0 +1,82 @@ +using Microsoft.Office.Tools.Ribbon; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace LubanAssistant +{ + public partial class AssistantTab + { + private string RootDefineFile + { + get => Properties.Settings.Default.rootDefineFile; + set + { + Properties.Settings.Default.rootDefineFile = value; + Properties.Settings.Default.Save(); + } + } + + private void AssistantTab_Load(object sender, RibbonUIEventArgs e) + { + } + + private bool CheckChooseRootDefineFile() + { + if (string.IsNullOrWhiteSpace(RootDefineFile) || !File.Exists(RootDefineFile)) + { + if (TryChooseRootDefineFile(out var rootDefineFile)) + { + RootDefineFile = rootDefineFile; + return true; + } + } + return false; + } + + private bool TryChooseRootDefineFile(out string rootDefineFile) + { + var dialog = new OpenFileDialog(); + dialog.DefaultExt = "xml"; + dialog.Filter = "root file (*.xml)|*.xml"; + dialog.Title = "Choose Root Xml File"; + dialog.CheckFileExists = true; + if (dialog.ShowDialog() == DialogResult.OK) + { + rootDefineFile = dialog.FileName; + return true; + } + rootDefineFile = null; + return false; + } + + private void BtnChooseRootFileClick(object sender, RibbonControlEventArgs e) + { + if (TryChooseRootDefineFile(out var rootDefineFile)) + { + RootDefineFile = rootDefineFile; + } + } + + private void BtnLoadClick(object sender, RibbonControlEventArgs e) + { + if (CheckChooseRootDefineFile()) + { + MessageBox.Show("load"); + } + } + + private void BtnSaveAllClick(object sender, RibbonControlEventArgs e) + { + MessageBox.Show("点击save"); + } + + private void BtnSaveSelectedClick(object sender, RibbonControlEventArgs e) + { + MessageBox.Show("点击save"); + } + } +} diff --git a/src/LubanAssistant/AssistantTab.resx b/src/LubanAssistant/AssistantTab.resx new file mode 100644 index 0000000..9bad2f5 --- /dev/null +++ b/src/LubanAssistant/AssistantTab.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/src/LubanAssistant/LubanAssistant.csproj b/src/LubanAssistant/LubanAssistant.csproj index 9702613..0d37324 100644 --- a/src/LubanAssistant/LubanAssistant.csproj +++ b/src/LubanAssistant/LubanAssistant.csproj @@ -1,4 +1,5 @@  + Excel + + 9.0 + + + ..\packages\CommandLineParser.2.8.0\lib\net461\CommandLine.dll + + + ..\packages\ExcelDataReader.3.6.0\lib\net45\ExcelDataReader.dll + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.5.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + ..\packages\NeoLua.1.3.13\lib\net47\Neo.Lua.dll + + + ..\packages\NLog.4.7.11\lib\net45\NLog.dll + + + ..\packages\Scriban.4.1.0\lib\netstandard2.0\Scriban.dll + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.5.0.0\lib\net45\System.Runtime.CompilerServices.Unsafe.dll + + + + + ..\packages\System.Text.Encodings.Web.5.0.1\lib\net461\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.5.0.2\lib\net461\System.Text.Json.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + ..\packages\YamlDotNet.11.2.1\lib\net45\YamlDotNet.dll + @@ -156,9 +214,309 @@ can be found. --> + + Source\EErrorCode.cs + + + Source\Utils\FileUtil.cs + + + Source\Utils\LogUtil.cs + + + Source\Utils\TypeUtil.cs + + + Source\Utils\XmlUtil.cs + + + Source\DataCreators\DataCreateException.cs + + + Source\DataCreators\ExcelDataCreator.cs + + + Source\DataCreators\ExcelNamedRowDataCreator.cs + + + Source\DataCreators\JsonDataCreator.cs + + + Source\DataCreators\MultiRowExcelDataCreator.cs + + + Source\DataCreators\StringDataCreator.cs + + + Source\DataSources\AbstractDataSource.cs + + + Source\DataSources\Excel\ExcelDataSource.cs + + + Source\DataSources\Excel\ExcelStream.cs + + + Source\DataSources\Excel\Sheet.cs + + + Source\DataSources\Json\JsonDataSource.cs + + + Source\Datas\DArray.cs + + + Source\Datas\DBean.cs + + + Source\Datas\DBool.cs + + + Source\Datas\DByte.cs + + + Source\Datas\DBytes.cs + + + Source\Datas\DDateTime.cs + + + Source\Datas\DDouble.cs + + + Source\Datas\DEnum.cs + + + Source\Datas\DFint.cs + + + Source\Datas\DFloat.cs + + + Source\Datas\DFlong.cs + + + Source\Datas\DFshort.cs + + + Source\Datas\DInt.cs + + + Source\Datas\DList.cs + + + Source\Datas\DLong.cs + + + Source\Datas\DMap.cs + + + Source\Datas\DSet.cs + + + Source\Datas\DShort.cs + + + Source\Datas\DString.cs + + + Source\Datas\DVector2.cs + + + Source\Datas\DVector3.cs + + + Source\Datas\DVector4.cs + + + Source\Datas\Record.cs + + + Source\DataVisitors\IDataActionVisitor.cs + + + Source\DataVisitors\IDataFuncVisitor.cs + + + Source\Defs\CfgDefTypeBase.cs + + + Source\Defs\DefBean.cs + + + Source\Defs\DefTable.cs + + + Source\RawDefs\CfgBean.cs + + + Source\RawDefs\CfgField.cs + + + Source\RawDefs\Defines.cs + + + Source\RawDefs\Group.cs + + + Source\RawDefs\Patch.cs + + + Source\RawDefs\ResourceInfo.cs + + + Source\RawDefs\Service.cs + + + Source\RawDefs\Table.cs + + + Source\TypeVisitors\DeepCompareTypeDefine.cs + + + Source\TypeVisitors\IsMultiData.cs + + + Source\TypeVisitors\IsNotSepTypeVisitor.cs + + + Source\Defs\RefTypeVisitor.cs + + + Source\Utils\DataUtil.cs + + + Source\Defs\CommonDefLoader.cs + + + Source\Defs\DefAssemblyBase.cs + + + Source\Defs\DefBeanBase.cs + + + Source\Defs\DefEnum.cs + + + Source\Defs\DefFieldBase.cs + + + Source\ELanguage.cs + + + Source\RawDefs\Bean.cs + + + Source\RawDefs\Field.cs + + + Source\RawDefs\PEnum.cs + + + Source\Types\TArray.cs + + + Source\Types\TBean.cs + + + Source\Types\TBool.cs + + + Source\Types\TByte.cs + + + Source\Types\TBytes.cs + + + Source\Types\TDateTime.cs + + + Source\Types\TDouble.cs + + + Source\Types\TEnum.cs + + + Source\Types\TFint.cs + + + Source\Types\TFloat.cs + + + Source\Types\TFlong.cs + + + Source\Types\TFshort.cs + + + Source\Types\TInt.cs + + + Source\Types\TList.cs + + + Source\Types\TLong.cs + + + Source\Types\TMap.cs + + + Source\Types\TSet.cs + + + Source\Types\TShort.cs + + + Source\Types\TString.cs + + + Source\Types\TText.cs + + + Source\Types\TType.cs + + + Source\Types\TVector2.cs + + + Source\Types\TVector3.cs + + + Source\Types\TVector4.cs + + + Source\TypeVisitors\AllFalseVisitor.cs + + + Source\TypeVisitors\AllTrueVisitor.cs + + + Source\TypeVisitors\DecoratorFuncVisitor.cs + + + Source\TypeVisitors\ITypeActionVisitor.cs + + + Source\TypeVisitors\ITypeFuncVisitor.cs + + + Source\Utils\DefUtil.cs + + + Source\IAgent.cs + + + Component + + + AssistantTab.cs + Code + + AssistantTab.cs + ResXFileCodeGenerator Resources.Designer.cs @@ -168,6 +526,9 @@ True Resources.resx + + + SettingsSingleFileGenerator Settings.Designer.cs @@ -175,7 +536,37 @@ True Settings.settings + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Code @@ -187,10 +578,22 @@ + + + 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + true + + + LubanAssistant_TemporaryKey.pfx + + + DB8575295993B72B55091F842E5704CA3A263CD5 + @@ -206,4 +609,12 @@ + + + 这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。 + + + + + \ No newline at end of file diff --git a/src/LubanAssistant/Properties/Settings.Designer.cs b/src/LubanAssistant/Properties/Settings.Designer.cs index de65ca4..fb719cf 100644 --- a/src/LubanAssistant/Properties/Settings.Designer.cs +++ b/src/LubanAssistant/Properties/Settings.Designer.cs @@ -1,10 +1,10 @@ //------------------------------------------------------------------------------ // -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 // -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 // //------------------------------------------------------------------------------ @@ -12,7 +12,7 @@ namespace LubanAssistant.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); @@ -22,5 +22,17 @@ namespace LubanAssistant.Properties { return defaultInstance; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string rootDefineFile { + get { + return ((string)(this["rootDefineFile"])); + } + set { + this["rootDefineFile"] = value; + } + } } } diff --git a/src/LubanAssistant/Properties/Settings.settings b/src/LubanAssistant/Properties/Settings.settings index 3964565..c3d99c0 100644 --- a/src/LubanAssistant/Properties/Settings.settings +++ b/src/LubanAssistant/Properties/Settings.settings @@ -1,7 +1,9 @@  - - - - - - + + + + + + + + \ No newline at end of file diff --git a/src/LubanAssistant/Source/AtomicLong.cs b/src/LubanAssistant/Source/AtomicLong.cs new file mode 100644 index 0000000..e4c77ad --- /dev/null +++ b/src/LubanAssistant/Source/AtomicLong.cs @@ -0,0 +1,36 @@ +using System.Threading; + +namespace Bright.Threading +{ + public class AtomicLong + { + private long _value; + public AtomicLong(long initValue = 0) + { + _value = initValue; + } + + public long IncrementAndGet() + { + return Interlocked.Add(ref _value, 1); + } + + public long AddAndGet(long step) + { + return Interlocked.Add(ref _value, step); + } + + public long GetAndAdd(long step) + { + return Interlocked.Add(ref _value, step); + } + + public long Value { get => Interlocked.Read(ref _value); set => Interlocked.Exchange(ref _value, value); } + + + public override string ToString() + { + return _value.ToString(); + } + } +} diff --git a/src/LubanAssistant/Source/CacheFileUtil.cs b/src/LubanAssistant/Source/CacheFileUtil.cs new file mode 100644 index 0000000..7a338ba --- /dev/null +++ b/src/LubanAssistant/Source/CacheFileUtil.cs @@ -0,0 +1,56 @@ +using Luban.Common.Protos; +using Luban.Common.Utils; +using Luban.Server.Common; +using System; +using System.Threading.Tasks; + +namespace Luban.Job.Common.Utils +{ + public static class CacheFileUtil + { + private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger(); + + public static string GenStringOrBytesMd5AndAddCache(string fileName, object content) + { + switch (content) + { + case string s: return GenMd5AndAddCache(fileName, s); + case byte[] bs: return GenMd5AndAddCache(fileName, bs); + default: throw new System.NotSupportedException(); + } + } + + public static string GenMd5AndAddCache(string fileName, string content, bool withUtf8Bom = false) + { + content = content.Replace("\r\n", "\n"); + byte[] bytes; + if (!withUtf8Bom) + { + bytes = System.Text.Encoding.UTF8.GetBytes(content); + } + else + { + //bytes = new byte[System.Text.Encoding.UTF8.GetByteCount(content) + 3 /* bom header */]; + //bytes[0] = 0xef; + //bytes[1] = 0xbb; + //bytes[2] = 0xbf; + //System.Text.Encoding.UTF8.GetBytes(content, bytes.AsSpan(3)); + throw new System.NotSupportedException(); + } + var md5 = FileUtil.CalcMD5(bytes); +#if !LUBAN_ASSISTANT + CacheManager.Ins.AddCache(fileName, md5, bytes); +#endif + return md5; + } + + public static string GenMd5AndAddCache(string fileName, byte[] bytes) + { + var md5 = FileUtil.CalcMD5(bytes); +#if !LUBAN_ASSISTANT + CacheManager.Ins.AddCache(fileName, md5, bytes); +#endif + return md5; + } + } +} diff --git a/src/LubanAssistant/Source/Collections/CollectionExtension.cs b/src/LubanAssistant/Source/Collections/CollectionExtension.cs new file mode 100644 index 0000000..2df013e --- /dev/null +++ b/src/LubanAssistant/Source/Collections/CollectionExtension.cs @@ -0,0 +1,260 @@ +using System; +using System.Collections.Generic; + +namespace Bright.Collections +{ + public static class CollectionExtension + { + + public static void AddRange(this IList dst, IEnumerable src) + { + foreach (var x in src) + { + dst.Add(x); + } + } + + public static bool TryAdd(this IDictionary map, TK key, TV value) + { + if (map.ContainsKey(key)) + { + return false; + } + map.Add(key, value); + return true; + } + + public static TV GetValueOrDefault(this IDictionary map, TK key, TV defaultValue = default) + { + return map.TryGetValue(key, out var value) ? value : defaultValue; + } + + public static TV GetOrAdd(this IDictionary map, TK key, Func creator) + { + if (map.TryGetValue(key, out var value)) + { + return value; + } + else + { + TV newValue = creator(key); + map.Add(key, newValue); + return newValue; + } + } + + public static TV GetOrAdd(this IDictionary map, TK key) where TV : new() + { + if (map.TryGetValue(key, out var value)) + { + return value; + } + else + { + TV newValue = new TV(); + map.Add(key, newValue); + return newValue; + } + } + + public static TV Get(this IDictionary map, TK key) where TV : class + { + return map.TryGetValue(key, out var value) ? value : null; + } + + public static bool Contains(this IDictionary map, TK key, TV value) + { + return map.Contains(new KeyValuePair(key, value)); + } + + public static void PutAll(this IDictionary dst, IDictionary src) + { + foreach (var e in src) + { + dst[e.Key] = e.Value; + } + } + + public static bool IsEmpty(this ICollection c) + { + return c.Count == 0; + } + + public static void Merge(this IDictionary dst, IDictionary src) + { + foreach (var e in src) + { + if (dst.TryGetValue(e.Key, out var v)) + { + dst[e.Key] = v + e.Value; + } + else + { + dst[e.Key] = e.Value; + } + } + } + + public static void Replace(this IDictionary dst, IDictionary src) + { + dst.Clear(); + dst.PutAll(src); + } + + public static int AddValue(this IDictionary dst, T key, int add) + { + if (dst.TryGetValue(key, out var value)) + { + return dst[key] = value + add; + } + else + { + return dst[key] = add; + } + } + + public static long AddValue(this IDictionary dst, T key, long add) + { + if (dst.TryGetValue(key, out var value)) + { + return dst[key] = value + add; + } + else + { + return dst[key] = add; + } + } + + public static int IncrementValue(this IDictionary dst, T key) + { + if (dst.TryGetValue(key, out var value)) + { + return dst[key] = value + 1; + } + else + { + return dst[key] = 1; + } + } + + public static long IncrementValue(this IDictionary dst, T key) + { + if (dst.TryGetValue(key, out var value)) + { + return dst[key] = value + 1; + } + else + { + return dst[key] = 1; + } + } + + public static bool ContainsAll(this IList a, IList b) + { + foreach (var x in b) + { + if (!a.Contains(x)) + { + return false; + } + } + return true; + } + + + public static void RemoveAll(this IList a, IList b) + { + foreach (var x in b) + { + a.Remove(x); + } + } + + public static bool DicEquals(this IDictionary a, IDictionary b) + { + if (a.Count != b.Count) + { + return false; + } + foreach (var e in a) + { + if (!(b.TryGetValue(e.Key, out var v) && v.Equals(e.Value))) + { + return false; + } + } + return true; + } + + public static void AddAll(this Dictionary data, Dictionary b) + { + foreach (var e in b) + { + data[e.Key] = e.Value; + } + } + + public static Dictionary Plus(this Dictionary data, TK key, TV value) + { + var newMap = new Dictionary(data) + { + [key] = value + }; + return newMap; + } + + + + public static T[] CopySubArray(this T[] data, int index, int length) + { + T[] result = new T[length]; + Array.Copy(data, index, result, 0, length); + return result; + } + + /// + /// 返回第一个满足条件的元素的index + /// + /// + /// + /// + /// 返回第一个满足条件的元素的index,如果没找到,返回 -1 + public static int IndexOfFirst(IEnumerable arr, Func predic) + { + int i = 0; + foreach (var x in arr) + { + if (predic(x)) + { + return i; + } + i++; + } + return -1; + } + + /// + /// 返回最后一个满足条件的元素的index + /// + /// + /// + /// + /// 返回最后一个满足条件的元素的index,如果没找到,返回 -1 + public static int IndexOfLast(IEnumerable arr, Func predic) + { + int i = -1; + foreach (var x in arr) + { + if (predic(x)) + { + ++i; + } + else + { + return i; + } + } + return -1; + } + } +} diff --git a/src/LubanAssistant/Source/Collections/CollectionUtil.cs b/src/LubanAssistant/Source/Collections/CollectionUtil.cs new file mode 100644 index 0000000..c468409 --- /dev/null +++ b/src/LubanAssistant/Source/Collections/CollectionUtil.cs @@ -0,0 +1,76 @@ +using System.Collections.Generic; +using System.Text; + +namespace Bright.Collections +{ + + public static class EmptyDictionary + { + public static Dictionary Empty { get; } = new Dictionary(); + } + + public static class CollectionUtil + { + + public static Dictionary SingletonMap(TK key, TV value) + { + var newMap = new Dictionary + { + { key, value } + }; + return newMap; + } + + public static string ToString(IDictionary m) + { + var sb = new StringBuilder(); + sb.Append('{'); + foreach (var e in m) + { + sb.Append(e.Key).Append(':').Append(e.Value).Append(','); + } + sb.Append('}'); + return sb.ToString(); + } + + public static void Replace(IDictionary dest, IDictionary src) + { + dest.Clear(); + foreach (var entry in src) + { + dest.Add(entry.Key, entry.Value); + } + + } + + public static void MergeIntValueDic(IDictionary dest, IDictionary src) + { + foreach (var entry in src) + { + if (dest.ContainsKey(entry.Key)) + { + dest[entry.Key] = dest[entry.Key] + entry.Value; + } + else + { + dest.Add(entry.Key, entry.Value); + } + } + } + + public static void MergeFloatValueDic(IDictionary dest, IDictionary src) + { + foreach (var entry in src) + { + if (dest.ContainsKey(entry.Key)) + { + dest[entry.Key] = dest[entry.Key] + entry.Value; + } + else + { + dest.Add(entry.Key, entry.Value); + } + } + } + } +} diff --git a/src/LubanAssistant/Source/Common/HashUtil.cs b/src/LubanAssistant/Source/Common/HashUtil.cs new file mode 100644 index 0000000..7f85275 --- /dev/null +++ b/src/LubanAssistant/Source/Common/HashUtil.cs @@ -0,0 +1,91 @@ +using System; + +namespace Bright.Common +{ + /// + /// 从 System.Collections.HashTable 源码抄来 + /// + public static class HashUtil + { + + private static readonly int[] primes = { + 3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919, + 1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591, + 17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437, + 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263, + 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369}; + + private const int HashPrime = 101; + + private const int MaxPrimeArrayLength = 0x7FEFFFFD; + + public static int GetPrime(int min) + { + if (min < 0) + throw new ArgumentException(); + + for (int i = 0; i < primes.Length; i++) + { + int prime = primes[i]; + if (prime >= min) return prime; + } + + //outside of our predefined table. + //compute the hard way. + for (int i = (min | 1); i < int.MaxValue; i += 2) + { + if (IsPrime(i) && ((i - 1) % HashPrime != 0)) + return i; + } + return min; + } + + public static bool IsPrime(int candidate) + { + if ((candidate & 1) != 0) + { + int limit = (int)Math.Sqrt(candidate); + for (int divisor = 3; divisor <= limit; divisor += 2) + { + if ((candidate % divisor) == 0) + return false; + } + return true; + } + return (candidate == 2); + } + + public static int GetMinPrime() + { + return primes[0]; + } + + // Returns size of hashtable to grow to. + public static int ExpandPrime(int oldSize) + { + int newSize = 2 * oldSize; + + // Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow. + // Note that this check works even when _items.Length overflowed thanks to the (uint) cast + if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize) + { + return MaxPrimeArrayLength; + } + + return GetPrime(newSize); + } + + public static int CalcHash(int x) + { + int hash = 17; + return hash * 23 + x; + } + + public static int CalcHash(int x, int y) + { + int hash = 17; + hash = hash * 23 + x; + return hash * 23 + y; + } + } +} diff --git a/src/LubanAssistant/Source/Common/MathUtil.cs b/src/LubanAssistant/Source/Common/MathUtil.cs new file mode 100644 index 0000000..d01fb56 --- /dev/null +++ b/src/LubanAssistant/Source/Common/MathUtil.cs @@ -0,0 +1,106 @@ +namespace Bright.Common +{ + public static class MathUtil + { + /// + /// 确保返回 x + y 的正确值, 如果溢出 抛出异常 + /// + /// + /// + /// + public static int AddExactly(int x, int y) + { + return checked(x + y); + } + + /// + /// 确保返回 x + y 的正确值, 如果溢出 抛出异常 + /// + /// + /// + /// + public static long AddExactly(long x, long y) + { + return checked(x + y); + } + + /// + /// 确保返回 x - y 的正确值, 如果溢出 抛出异常 + /// + /// + /// + /// + public static int SubExcatly(int x, int y) + { + return checked(x - y); + } + + /// + /// 确保返回 x - y 的正确值, 如果溢出 抛出异常 + /// + /// + /// + /// + public static long SubExactly(long x, long y) + { + return checked(x - y); + } + + /// + /// 确保返回 x * y 的正确值, 如果溢出 抛出异常 + /// + /// + /// + /// + public static int MultifyExactly(int x, int y) + { + return checked(x * y); + } + + /// + /// 确保返回 x * y 的正确值, 如果溢出 抛出异常 + /// + /// + /// + /// + public static long MultifyExactly(long x, long y) + { + return checked(x * y); + } + + /// + /// 取 >= x/y 的最小整数 + /// + /// 除数 + /// 被除数 + /// + public static int Ceil(int x, int y) + { + return (x + y - 1) / y; + } + + /// + /// 取 >= x/y 的最小整数 + /// + /// 除数 + /// 被除数 + /// + public static long Ceil(long x, long y) + { + return (x + y - 1) / y; + } + + /// + /// 返回 [min, max] 之间的数 + /// + /// + /// + /// + /// + public static int Clamp(int value, int min, int max) + { + return value < min ? min : (value > max ? max : value); + } + + } +} diff --git a/src/LubanAssistant/Source/Common/SerializationUtil.cs b/src/LubanAssistant/Source/Common/SerializationUtil.cs new file mode 100644 index 0000000..a724085 --- /dev/null +++ b/src/LubanAssistant/Source/Common/SerializationUtil.cs @@ -0,0 +1,214 @@ +using System.Collections.Generic; +using System.Numerics; +using Bright.Serialization; + +namespace Bright.Common +{ + public static class SerializationUtil + { + public static void Serialize(ByteBuf os, List list) where T : ISerializable + { + os.WriteSize(list.Count); + foreach (var e in list) + { + e.Serialize(os); + } + } + + public static void Deserialize(ByteBuf os, List list) where T : ISerializable, new() + { + int n = os.ReadSize(); + for (int i = 0; i < n; i++) + { + T e = new T(); + e.Deserialize(os); + list.Add(e); + } + } + + public static void Serialize(ByteBuf os, List list) + { + os.WriteSize(list.Count); + foreach (var e in list) + { + os.WriteString(e); + } + } + + public static void Deserialize(ByteBuf os, List list) + { + int n = os.ReadSize(); + for (int i = 0; i < n; i++) + { + list.Add(os.ReadString()); + } + } + + public static unsafe int FloatToIntBits(float f) + { + return *((int*)&f); + } + + public static void SerializeBool(ByteBuf buf, bool x) + { + buf.WriteBool(x); + } + + public static bool DeserializeBool(ByteBuf buf) + { + return buf.ReadBool(); + } + + public static void SerializeByte(ByteBuf buf, byte x) + { + buf.WriteByte(x); + } + + public static byte DeserializeByte(ByteBuf buf) + { + return buf.ReadByte(); + } + + public static void SerializeShort(ByteBuf buf, short x) + { + buf.WriteShort(x); + } + + public static short DeserializeShort(ByteBuf buf) + { + return buf.ReadShort(); + } + + public static void SerializeFshort(ByteBuf buf, short x) + { + buf.WriteFshort(x); + } + + public static short DeserializeFshort(ByteBuf buf) + { + return buf.ReadFshort(); + } + + public static void SerializeInt(ByteBuf buf, int x) + { + buf.WriteInt(x); + } + + public static int DeserializeInt(ByteBuf buf) + { + return buf.ReadInt(); + } + + public static void SerializeFint(ByteBuf buf, int x) + { + buf.WriteFint(x); + } + + public static int DeserializeFint(ByteBuf buf) + { + return buf.ReadFint(); + } + + public static void SerializeLong(ByteBuf buf, long x) + { + buf.WriteLong(x); + } + + public static long DeserializeLong(ByteBuf buf) + { + return buf.ReadLong(); + } + + public static void SerializeFlong(ByteBuf buf, long x) + { + buf.WriteFlong(x); + } + + public static long DeserializeFlong(ByteBuf buf) + { + return buf.ReadFlong(); + } + + public static void SerializeFloat(ByteBuf buf, float x) + { + buf.WriteFloat(x); + } + + public static float DeserializeFloat(ByteBuf buf) + { + return buf.ReadFloat(); + } + + public static void SerializeDouble(ByteBuf buf, double x) + { + buf.WriteDouble(x); + } + + public static double DeserializeDouble(ByteBuf buf) + { + return buf.ReadDouble(); + } + + public static void SerializeString(ByteBuf buf, string x) + { + buf.WriteString(x); + } + + public static string DeserializeString(ByteBuf buf) + { + return buf.ReadString(); + } + + public static void SerializeBytes(ByteBuf buf, byte[] x) + { + buf.WriteBytes(x); + } + + public static byte[] DeserializeBytes(ByteBuf buf) + { + return buf.ReadBytes(); + } + + public static void SerializeVector2(ByteBuf buf, Vector2 x) + { + buf.WriteVector2(x); + } + + public static Vector2 DeserializeVector2(ByteBuf buf) + { + return buf.ReadVector2(); + } + + public static void SerializeVector3(ByteBuf buf, Vector3 x) + { + buf.WriteVector3(x); + } + + public static Vector3 DeserializeVector3(ByteBuf buf) + { + return buf.ReadVector3(); + } + + public static void SerializeVector4(ByteBuf buf, Vector4 x) + { + buf.WriteVector4(x); + } + + public static Vector4 DeserializeVector4(ByteBuf buf) + { + return buf.ReadVector4(); + } + + public static void SerializeBean(ByteBuf buf, T x) where T : BeanBase + { + x.Serialize(buf); + } + + public static T DeserializeBean(ByteBuf buf) where T : BeanBase, new() + { + var x = new T(); + x.Deserialize(buf); + return x; + } + } +} diff --git a/src/LubanAssistant/Source/Common/StringUtil.cs b/src/LubanAssistant/Source/Common/StringUtil.cs new file mode 100644 index 0000000..c91f2a1 --- /dev/null +++ b/src/LubanAssistant/Source/Common/StringUtil.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.Text; + +namespace Bright.Common +{ + public static class StringUtil + { + public static string ToStr(object o) + { + return ToStr(o, new StringBuilder()); + } + + public static string ToStr(object o, StringBuilder sb) + { + foreach (var p in o.GetType().GetFields()) + { + + sb.Append($"{p.Name} = {p.GetValue(o)},"); + } + + foreach (var p in o.GetType().GetProperties()) + { + sb.Append($"{p.Name} = {p.GetValue(o)},"); + } + return sb.ToString(); + } + + public static string ArrayToString(T[] arr) + { + return "[" + string.Join(",", arr) + "]"; + } + + + public static string CollectionToString(IEnumerable arr) + { + return "[" + string.Join(",", arr) + "]"; + } + + + public static string CollectionToString(IDictionary dic) + { + var sb = new StringBuilder('{'); + foreach (var e in dic) + { + sb.Append(e.Key).Append(':'); + sb.Append(e.Value).Append(','); + } + sb.Append('}'); + return sb.ToString(); + } + } +} diff --git a/src/LubanAssistant/Source/Common/TimeUtil.cs b/src/LubanAssistant/Source/Common/TimeUtil.cs new file mode 100644 index 0000000..32a1b91 --- /dev/null +++ b/src/LubanAssistant/Source/Common/TimeUtil.cs @@ -0,0 +1,156 @@ +using System; +using System.Threading; + +namespace Bright.Time +{ + public static class TimeUtil + { + + public static readonly long TIMEZONE_OFFSET_MILLS = (long)TimeZoneInfo.Local.GetUtcOffset(DateTimeOffset.FromUnixTimeSeconds(0)).TotalMilliseconds; + public static readonly int TIMEZONE_OFFSET = (int)TimeZoneInfo.Local.GetUtcOffset(DateTimeOffset.FromUnixTimeSeconds(0)).TotalSeconds; + + public const long DAY_MILLISECONDS = 86400000; + public const int DAY_SECONDS = 86400; + public const int HOUR_SECONDS = 3600; + public const int MINUTE_SECONDS = 60; + + public const int WEEKDAY_OF_19700101 = 3; + + public const long HOUR_MILLISECONDS = 3600000; + public const long MINUTE_MILLISECONDS = 60000; + public const long WEEK_MILLISECONDS = DAY_MILLISECONDS * 7; + + public static bool IsSameDay(int time1, int time2) + { + return (time1 + TIMEZONE_OFFSET) / DAY_SECONDS == (time2 + TIMEZONE_OFFSET) / DAY_SECONDS; + } + +#if DEBUG + private static long s_millisTimeOffsetForTest; + + /// + /// 用于调整服务器内时间,只对测试版本生效 + /// + public static long MillisTimeOffsetForTest + { + get + { + return Interlocked.Read(ref s_millisTimeOffsetForTest); + } + set + { + Interlocked.Exchange(ref s_millisTimeOffsetForTest, value); + } + } + + + public static long NowMillis => DateTimeOffset.Now.ToUnixTimeMilliseconds() + s_millisTimeOffsetForTest; + + public static int Now => (int)(DateTimeOffset.Now.ToUnixTimeSeconds() + s_millisTimeOffsetForTest / 1000); + +#else + public static long NowMillis => DateTimeOffset.Now.ToUnixTimeMilliseconds(); + + public static int Now => (int)DateTimeOffset.Now.ToUnixTimeSeconds(); +#endif + + public static bool IsSameWeek(int time1, int time2) + { + return GetMondayZeroTimeOfThisWeek(time1) == GetMondayZeroTimeOfThisWeek(time2); + } + + public static int TodayZeroTime() + { + return TodayZeroTime(Now); + } + + public static int TodayZeroTime(int time) + { + return (time + TIMEZONE_OFFSET) / DAY_SECONDS * DAY_SECONDS - TIMEZONE_OFFSET; + } + + + public static int TomorrowZeroTime() + { + return TomorrowZeroTime(Now); + } + + public static int TomorrowZeroTime(int time) + { + return TodayZeroTime(time) + DAY_SECONDS; + } + + public static int AnotherDayZeroTime(int time, int offsetDayNum) + { + return TodayZeroTime(time) + DAY_SECONDS * offsetDayNum; + } + + public static bool IsContinuesDay(int time1, int time2) + { + return (time1 + TIMEZONE_OFFSET) / DAY_SECONDS + 1 == (time2 + TIMEZONE_OFFSET) / DAY_SECONDS; + } + + public static int DayOffset(int time1, int time2) + { + int a = (time1 + TIMEZONE_OFFSET) / DAY_SECONDS; + int b = (time2 + TIMEZONE_OFFSET) / DAY_SECONDS; + return Math.Abs(a - b); + } + + public static int GetSecondsFromTodayZeroTime(int time) + { + return time - TodayZeroTime(time); + } + + public static int GetHourOfToday(int time) + { + return (time + TIMEZONE_OFFSET) % DAY_SECONDS / HOUR_SECONDS; + } + + public static int GetMinuteOfToday(int time) + { + long interval = time - TodayZeroTime(time); + long left = interval % HOUR_SECONDS; + return (int)(left / MINUTE_SECONDS); + } + + public static long GetSecondsOfDay(int hour, int minute) + { + return hour * HOUR_SECONDS + minute * MINUTE_SECONDS; + } + + public static int GetWeekDay(int time) + { + return ((time + TIMEZONE_OFFSET) / DAY_SECONDS + WEEKDAY_OF_19700101) % 7; + } + + + public static int GetMondayZeroTimeOfNextWeek(int time) + { + return TodayZeroTime(time) + DAY_SECONDS * (7 - GetWeekDay(time)); + } + + public static int GetMondayZeroTimeOfNextWeek() + { + return GetMondayZeroTimeOfNextWeek(Now); + } + + public static int GetMondayZeroTimeOfThisWeek(int time) + { + return TodayZeroTime(time) - DAY_SECONDS * GetWeekDay(time); + } + + /** + * weekday 0 - 6 对应 周一 到 周日 + */ + public static long GetSecondsOfWeek(int weekday, int hour, int minute, int second) + { + return (weekday * 86400 + hour * 3600 + minute * 60 + second) * 1000L; + } + + public static long ToMills(float seconds) + { + return (long)((double)seconds * 1000); + } + } +} diff --git a/src/LubanAssistant/Source/Common/ValueUtil.cs b/src/LubanAssistant/Source/Common/ValueUtil.cs new file mode 100644 index 0000000..3b16bd2 --- /dev/null +++ b/src/LubanAssistant/Source/Common/ValueUtil.cs @@ -0,0 +1,22 @@ +namespace Bright.Common +{ + public static class ValueUtil + { + public static void Swap(ref T a, ref T b) + { + T temp = a; + a = b; + b = temp; + } + + public static void LockAndSwap(object locker, ref T a, ref T b) + { + lock (locker) + { + T temp = a; + a = b; + b = temp; + } + } + } +} diff --git a/src/LubanAssistant/Source/DataSources/DataSourceFactory.cs b/src/LubanAssistant/Source/DataSources/DataSourceFactory.cs new file mode 100644 index 0000000..65457da --- /dev/null +++ b/src/LubanAssistant/Source/DataSources/DataSourceFactory.cs @@ -0,0 +1,43 @@ +using System; +using System.IO; + +namespace Luban.Job.Cfg.DataSources +{ + static class DataSourceFactory + { + public static readonly string[] validDataSourceSuffixes = new string[] + { + ".xlsx", + ".xls", + ".csv", + ".xml", + ".lua", + ".json", + ".yml", + ".bin", + }; + + public static AbstractDataSource Create(string url, string sheetName, Stream stream) + { + try + { + string ext = url.Contains(".") ? Path.GetExtension(url)?.Substring(1) : url; + AbstractDataSource source; + switch (ext) + { + case "csv": + case "xls": + case "xlsx": source = new Excel.ExcelDataSource(); break; + case "json": source = new Json.JsonDataSource(); break; + default: throw new Exception($"不支持的文件类型:{url}"); + } + source.Load(url, sheetName, stream); + return source; + } + catch (Exception e) + { + throw new Exception($"文件{url} 加载失败", e); + } + } + } +} diff --git a/src/LubanAssistant/Source/Datas/DText.cs b/src/LubanAssistant/Source/Datas/DText.cs new file mode 100644 index 0000000..c883d0a --- /dev/null +++ b/src/LubanAssistant/Source/Datas/DText.cs @@ -0,0 +1,54 @@ +using Luban.Job.Cfg.DataVisitors; + +namespace Luban.Job.Cfg.Datas +{ + public class DText : DType + { + public const string KEY_NAME = "key"; + public const string TEXT_NAME = "text"; + + public string Key { get; } + + private readonly string _rawValue; + + public string RawValue => _rawValue; + + public override string TypeName => "text"; + + public DText(string key, string x) + { + Key = key; + _rawValue = x; + } + + public override void Apply(IDataActionVisitor visitor, T x) + { + visitor.Accept(this, x); + } + + public override void Apply(IDataActionVisitor visitor, T1 x, T2 y) + { + visitor.Accept(this, x, y); + } + + public override TR Apply(IDataFuncVisitor visitor) + { + return visitor.Accept(this); + } + + public override TR Apply(IDataFuncVisitor visitor, T x) + { + return visitor.Accept(this, x); + } + + public override bool Equals(object obj) + { + return obj is DText o && o._rawValue == this._rawValue && o.Key == this.Key; + } + + public override int GetHashCode() + { + return _rawValue.GetHashCode(); + } + } +} diff --git a/src/LubanAssistant/Source/Datas/DType.cs b/src/LubanAssistant/Source/Datas/DType.cs new file mode 100644 index 0000000..7a8399b --- /dev/null +++ b/src/LubanAssistant/Source/Datas/DType.cs @@ -0,0 +1,28 @@ + +using Luban.Job.Cfg.DataVisitors; + +namespace Luban.Job.Cfg.Datas +{ + public abstract class DType + { + public abstract void Apply(IDataActionVisitor visitor, T x); + + public abstract void Apply(IDataActionVisitor visitor, T1 x, T2 y); + + public abstract TR Apply(IDataFuncVisitor visitor); + + public abstract TR Apply(IDataFuncVisitor visitor, T x); + + public abstract string TypeName { get; } + } + + public abstract class DType : DType + { + public T Value { get; } + + protected DType(T value) + { + Value = value; + } + } +} diff --git a/src/LubanAssistant/Source/Defs/CfgDefLoader.cs b/src/LubanAssistant/Source/Defs/CfgDefLoader.cs new file mode 100644 index 0000000..d178997 --- /dev/null +++ b/src/LubanAssistant/Source/Defs/CfgDefLoader.cs @@ -0,0 +1,471 @@ +using Bright.Collections; +using Luban.Common.Utils; +using Luban.Job.Cfg.Datas; +using Luban.Job.Cfg.DataSources.Excel; +using Luban.Job.Cfg.RawDefs; +using Luban.Job.Cfg.Utils; +using Luban.Job.Common.Defs; +using Luban.Job.Common.RawDefs; +using Luban.Job.Common.Types; +using Luban.Job.Common.Utils; +using Luban.Server.Common; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Luban.Job.Cfg.Defs +{ + class CfgDefLoader : CommonDefLoader + { + private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger(); + + private readonly List _importExcelTableFiles = new(); + private readonly List _importExcelEnumFiles = new(); + private readonly List _importExcelBeanFiles = new(); + + + private readonly List _patches = new(); + + private readonly List _cfgTables = new List
(); + + private readonly List _cfgServices = new List(); + + private readonly List _cfgGroups = new List(); + + private readonly List _defaultGroups = new List(); + + public CfgDefLoader(IAgent agent) : base(agent) + { + RegisterRootDefineHandler("importexcel", AddImportExcel); + RegisterRootDefineHandler("patch", AddPatch); + RegisterRootDefineHandler("service", AddService); + RegisterRootDefineHandler("group", AddGroup); + + RegisterModuleDefineHandler("table", AddTable); + + + IsBeanFieldMustDefineId = false; + } + + public Defines BuildDefines() + { + return new Defines() + { + TopModule = TopModule, + Patches = _patches, + Enums = _enums, + Beans = _beans, + Tables = _cfgTables, + Services = _cfgServices, + Groups = _cfgGroups, + }; + } + + private static readonly List _excelImportRequireAttrs = new List { "name", "type" }; + private void AddImportExcel(XElement e) + { + ValidAttrKeys(RootXml, e, null, _excelImportRequireAttrs); + var importName = XmlUtil.GetRequiredAttribute(e, "name"); + if (string.IsNullOrWhiteSpace(importName)) + { + throw new Exception("importexcel 属性name不能为空"); + } + var type = XmlUtil.GetRequiredAttribute(e, "type"); + if (string.IsNullOrWhiteSpace(type)) + { + throw new Exception($"importexcel name:'{importName}' type属性不能为空"); + } + switch (type) + { + case "table": this._importExcelTableFiles.Add(importName); break; + case "enum": this._importExcelEnumFiles.Add(importName); break; + case "bean": this._importExcelBeanFiles.Add(importName); break; + default: throw new Exception($"importexcel name:'{importName}' type:'{type}' 不合法. 有效值为 table|enum|bean"); + } + } + + private static readonly List _patchRequireAttrs = new List { "name" }; + private void AddPatch(XElement e) + { + ValidAttrKeys(RootXml, e, null, _patchRequireAttrs); + var patchName = e.Attribute("name").Value; + if (string.IsNullOrWhiteSpace(patchName)) + { + throw new Exception("patch 属性name不能为空"); + } + if (this._patches.Any(b => b.Name == patchName)) + { + throw new Exception($"patch '{patchName}' 重复"); + } + _patches.Add(new Patch(patchName)); + } + + private static readonly List _groupOptionalAttrs = new List { "default" }; + private static readonly List _groupRequireAttrs = new List { "name" }; + + private void AddGroup(XElement e) + { + ValidAttrKeys(RootXml, e, _groupOptionalAttrs, _groupRequireAttrs); + List groupNames = CreateGroups(e.Attribute("name").Value); + + foreach (var g in groupNames) + { + if (_cfgGroups.Any(cg => cg.Names.Contains(g))) + { + throw new Exception($"group名:'{g}' 重复"); + } + } + + if (XmlUtil.GetOptionBoolAttribute(e, "default")) + { + this._defaultGroups.AddRange(groupNames); + } + _cfgGroups.Add(new Group() { Names = groupNames }); + } + + private void FillValueValidator(CfgField f, string attrValue, string validatorName) + { + if (!string.IsNullOrWhiteSpace(attrValue)) + { + var validator = new Validator() { Type = validatorName, Rule = attrValue }; + f.Validators.Add(validator); + f.ValueValidators.Add(validator); + } + } + + private void FillValidators(string defineFile, string key, string attr, List result) + { + if (!string.IsNullOrWhiteSpace(attr)) + { +#if !LUBAN_ASSISTANT + foreach (var validatorStr in attr.Split('#', StringSplitOptions.RemoveEmptyEntries)) +#else + foreach (var validatorStr in attr.Split('#')) +#endif + { + var sepIndex = validatorStr.IndexOf(':'); + if (sepIndex <= 0) + { + throw new Exception($"定义文件:{defineFile} key:'{key}' attr:'{attr}' 不是合法的 validator 定义 (key1:value1#key2:value2 ...)"); + } +#if !LUBAN_ASSISTANT + result.Add(new Validator() { Type = validatorStr[..sepIndex], Rule = validatorStr[(sepIndex + 1)..] }); +#else + result.Add(new Validator() { Type = validatorStr.Substring(0, sepIndex), Rule = validatorStr.Substring(sepIndex + 1, validatorStr.Length - sepIndex - 1) }); +#endif + } + } + } + + private readonly List _serviceAttrs = new List { "name", "manager", "group" }; + + private void AddService(XElement e) + { + var name = XmlUtil.GetRequiredAttribute(e, "name"); + var manager = XmlUtil.GetRequiredAttribute(e, "manager"); + List groups = CreateGroups(XmlUtil.GetOptionalAttribute(e, "group")); + var refs = new List(); + + s_logger.Trace("service name:{name} manager:{manager}", name, manager); + ValidAttrKeys(RootXml, e, _serviceAttrs, _serviceAttrs); + foreach (XElement ele in e.Elements()) + { + string tagName = ele.Name.LocalName; + s_logger.Trace("service {service_name} tag: {name} {value}", name, tagName, ele); + switch (tagName) + { + case "ref": + { + refs.Add(XmlUtil.GetRequiredAttribute(ele, "name")); + break; + } + default: + { + throw new Exception($"service:'{name}' tag:'{tagName}' 非法"); + } + } + } + if (!ValidGroup(groups, out var invalidGroup)) + { + throw new Exception($"service:'{name}' group:'{invalidGroup}' 不存在"); + } + _cfgServices.Add(new Service() { Name = name, Manager = manager, Groups = groups, Refs = refs }); + } + + + private readonly Dictionary _name2CfgTable = new Dictionary(); + + private static List CreateGroups(string s) + { + return s.Split(',', ';').Select(x => x.Trim()).Where(x => !string.IsNullOrWhiteSpace(x)).ToList(); + } + + private bool ValidGroup(List groups, out string invalidGroup) + { + foreach (var g in groups) + { + if (!this._cfgGroups.Any(cg => cg.Names.Contains(g))) + { + invalidGroup = g; + return false; + } + } + invalidGroup = null; + return true; + } + + private ETableMode ConvertMode(string defineFile, string tableName, string modeStr, string indexStr) + { + ETableMode mode; + switch (modeStr) + { + case "one": + { + if (!string.IsNullOrWhiteSpace(indexStr)) + { + throw new Exception($"定义文件:{defineFile} table:'{tableName}' mode=one 是单例表,不支持定义index属性"); + } + mode = ETableMode.ONE; + break; + } + case "map": + { + //if ((string.IsNullOrWhiteSpace(indexStr) || indexStr.Split(',').Length != 1)) + //{ + // throw new Exception($"定义文件:{CurImportFile} table:{tableName} 是单键表,必须在index属性里指定1个key"); + //} + mode = ETableMode.MAP; + break; + } + case "": + { + mode = ETableMode.MAP; + break; + } + default: + { + throw new ArgumentException($"不支持的 mode:{modeStr}"); + } + } + return mode; + } + + private readonly List _tableOptionalAttrs = new List { "index", "mode", "group", "patch_input", "comment", "define_from_file" }; + private readonly List _tableRequireAttrs = new List { "name", "value", "input" }; + + private void AddTable(string defineFile, XElement e) + { + ValidAttrKeys(defineFile, e, _tableOptionalAttrs, _tableRequireAttrs); + string name = XmlUtil.GetRequiredAttribute(e, "name"); + string module = CurNamespace; + string valueType = XmlUtil.GetRequiredAttribute(e, "value"); + bool defineFromFile = XmlUtil.GetOptionBoolAttribute(e, "define_from_file"); + string index = XmlUtil.GetOptionalAttribute(e, "index"); + string group = XmlUtil.GetOptionalAttribute(e, "group"); + string comment = XmlUtil.GetOptionalAttribute(e, "comment"); + string input = XmlUtil.GetRequiredAttribute(e, "input"); + string patchInput = XmlUtil.GetOptionalAttribute(e, "patch_input"); + string mode = XmlUtil.GetOptionalAttribute(e, "mode"); + string tags = XmlUtil.GetOptionalAttribute(e, "tags"); + AddTable(defineFile, name, module, valueType, index, mode, group, comment, defineFromFile, input, patchInput, tags); + } + + private void AddTable(string defineFile, string name, string module, string valueType, string index, string mode, string group, + string comment, bool defineFromExcel, string input, string patchInput, string tags) + { + var p = new Table() + { + Name = name, + Namespace = module, + ValueType = valueType, + LoadDefineFromFile = defineFromExcel, + Index = index, + Groups = CreateGroups(group), + Comment = comment, + Mode = ConvertMode(defineFile, name, mode, index), + Tags = tags, + }; + + if (p.Groups.Count == 0) + { + p.Groups = this._defaultGroups; + } + else if (!ValidGroup(p.Groups, out var invalidGroup)) + { + throw new Exception($"定义文件:{defineFile} table:'{p.Name}' group:'{invalidGroup}' 不存在"); + } + p.InputFiles.AddRange(input.Split(',').Select(s => s.Trim()).Where(s => !string.IsNullOrWhiteSpace(s))); + + if (!string.IsNullOrWhiteSpace(patchInput)) + { + foreach (var subPatchStr in patchInput.Split('|').Select(s => s.Trim()).Where(s => !string.IsNullOrWhiteSpace(s))) + { + var nameAndDirs = subPatchStr.Split(':'); + if (nameAndDirs.Length != 2) + { + throw new Exception($"定义文件:{defineFile} table:'{p.Name}' patch_input:'{subPatchStr}' 定义不合法"); + } + var patchDirs = nameAndDirs[1].Split(',', ';').ToList(); + if (!p.PatchInputFiles.TryAdd(nameAndDirs[0], patchDirs)) + { + throw new Exception($"定义文件:{defineFile} table:'{p.Name}' patch_input:'{subPatchStr}' 子patch:'{nameAndDirs[0]}' 重复"); + } + } + } + + _cfgTables.Add(p); + } + + + private static readonly List _fieldOptionalAttrs = new() + { + "index", + "sep", + "validator", + "key_validator", + "value_validator", + "ref", + "path", + "range", + "multi_rows", + "group", + "res", + "convert", + "comment", + "tags", + "default", + "orientation", + }; + + private static readonly List _fieldRequireAttrs = new List { "name", "type" }; + + protected override Field CreateField(string defineFile, XElement e) + { + ValidAttrKeys(defineFile, e, _fieldOptionalAttrs, _fieldRequireAttrs); + + return CreateField(defineFile, XmlUtil.GetRequiredAttribute(e, "name"), + XmlUtil.GetRequiredAttribute(e, "type"), + XmlUtil.GetOptionalAttribute(e, "index"), + XmlUtil.GetOptionalAttribute(e, "sep"), + XmlUtil.GetOptionBoolAttribute(e, "multi_rows"), + XmlUtil.GetOptionalAttribute(e, "group"), + XmlUtil.GetOptionalAttribute(e, "res"), + XmlUtil.GetOptionalAttribute(e, "convert"), + XmlUtil.GetOptionalAttribute(e, "comment"), + XmlUtil.GetOptionalAttribute(e, "ref"), + XmlUtil.GetOptionalAttribute(e, "path"), + XmlUtil.GetOptionalAttribute(e, "range"), + XmlUtil.GetOptionalAttribute(e, "key_validator"), + XmlUtil.GetOptionalAttribute(e, "value_validator"), + XmlUtil.GetOptionalAttribute(e, "validator"), + XmlUtil.GetOptionalAttribute(e, "tags"), + false, + DefUtil.ParseOrientation(XmlUtil.GetOptionalAttribute(e, "orientation")) + ); + } + + private Field CreateField(string defileFile, string name, string type, string index, string sep, bool isMultiRow, string group, string resource, string converter, + string comment, string refs, string path, string range, string keyValidator, string valueValidator, string validator, string tags, + bool ignoreNameValidation, bool isRowOrient) + { + var f = new CfgField() + { + Name = name, + Index = index, + Sep = sep, + IsMultiRow = isMultiRow, + Groups = CreateGroups(group), + Resource = resource, + Converter = converter, + Comment = comment, + Tags = tags, + IgnoreNameValidation = ignoreNameValidation, + IsRowOrient = isRowOrient, + }; + + // 字段与table的默认组不一样。 + // table 默认只属于default=1的组 + // 字段默认属于所有组 + if (f.Groups.Count == 0) + { + + } + else if (!ValidGroup(f.Groups, out var invalidGroup)) + { + throw new Exception($"定义文件:{defileFile} field:'{name}' group:'{invalidGroup}' 不存在"); + } + f.Type = type; + + + FillValueValidator(f, refs, "ref"); + FillValueValidator(f, path, "path"); // (ue4|unity|normal|regex);xxx;xxx + FillValueValidator(f, range, "range"); + + FillValidators(defileFile, "key_validator", keyValidator, f.KeyValidators); + FillValidators(defileFile, "value_validator", valueValidator, f.ValueValidators); + FillValidators(defileFile, "validator", validator, f.Validators); + return f; + } + + private static readonly List _beanOptinsAttrs = new List { "value_type", "alias", "sep", "comment", "tags" }; + private static readonly List _beanRequireAttrs = new List { "name" }; + + protected override void AddBean(string defineFile, XElement e, string parent) + { + ValidAttrKeys(defineFile, e, _beanOptinsAttrs, _beanRequireAttrs); + + var b = new CfgBean() + { + Name = XmlUtil.GetRequiredAttribute(e, "name"), + Namespace = CurNamespace, + Parent = parent.Length > 0 ? parent : "", + TypeId = 0, + IsSerializeCompatible = true, + IsValueType = XmlUtil.GetOptionBoolAttribute(e, "value_type"), + Alias = XmlUtil.GetOptionalAttribute(e, "alias"), + Sep = XmlUtil.GetOptionalAttribute(e, "sep"), + Comment = XmlUtil.GetOptionalAttribute(e, "comment"), + Tags = XmlUtil.GetOptionalAttribute(e, "tags"), + }; + var childBeans = new List(); + + bool defineAnyChildBean = false; + foreach (XElement fe in e.Elements()) + { + switch (fe.Name.LocalName) + { + case "var": + { + if (defineAnyChildBean) + { + throw new LoadDefException($"定义文件:{defineFile} 类型:{b.FullName} 的多态子bean必须在所有成员字段 之前定义"); + } + b.Fields.Add(CreateField(defineFile, fe)); ; + break; + } + case "bean": + { + defineAnyChildBean = true; + childBeans.Add(fe); + break; + } + default: + { + throw new LoadDefException($"定义文件:{defineFile} 类型:{b.FullName} 不支持 tag:{fe.Name}"); + } + } + } + s_logger.Trace("add bean:{@bean}", b); + _beans.Add(b); + + var fullname = b.FullName; + foreach (var cb in childBeans) + { + AddBean(defineFile, cb, fullname); + } + } + } +} diff --git a/src/LubanAssistant/Source/Defs/DefAssembly.cs b/src/LubanAssistant/Source/Defs/DefAssembly.cs new file mode 100644 index 0000000..250a29f --- /dev/null +++ b/src/LubanAssistant/Source/Defs/DefAssembly.cs @@ -0,0 +1,290 @@ +using Bright.Collections; +using Luban.Job.Cfg.Datas; +using Luban.Job.Cfg.RawDefs; +using Luban.Job.Cfg.TypeVisitors; +using Luban.Job.Common.Defs; +using Luban.Server.Common; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; + +namespace Luban.Job.Cfg.Defs +{ + public class TableDataInfo + { + public List MainRecords { get; } + + public List PatchRecords { get; } + + public List FinalRecords { get; set; } + + public Dictionary FinalRecordMap { get; set; } + + public TableDataInfo(List mainRecords, List patchRecords) + { + MainRecords = mainRecords; + PatchRecords = patchRecords; + } + } + + public class DefAssembly : DefAssemblyBase + { + private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger(); + + public new static DefAssembly LocalAssebmly { get => (DefAssembly)DefAssemblyBase.LocalAssebmly; set => DefAssemblyBase.LocalAssebmly = value; } + + public Service CfgTargetService { get; private set; } + + private readonly string _patchName; + private readonly List _excludeTags; + + public Patch TargetPatch { get; private set; } + + public TimeZoneInfo TimeZone { get; } + + public DefAssembly(string patchName, TimeZoneInfo timezone, List excludeTags, IAgent agent) + { + this._patchName = patchName; + this.TimeZone = timezone; + this._excludeTags = excludeTags; + this.Agent = agent; + } + + public bool NeedExport(List groups) + { + if (groups.Count == 0) + { + return true; + } + return groups.Any(g => CfgTargetService.Groups.Contains(g)); + } + + private readonly List _patches = new List(); + + private readonly List _cfgServices = new List(); + + private readonly ConcurrentDictionary _recordsByTables = new(); + + public Dictionary CfgTables { get; } = new Dictionary(); + + public Patch GetPatch(string name) + { + return _patches.Find(b => b.Name == name); + } + + public void AddCfgTable(DefTable table) + { + if (!CfgTables.TryAdd(table.FullName, table)) + { + throw new Exception($"table:'{table.FullName}' duplicated"); + } + } + + public DefTable GetCfgTable(string name) + { + return CfgTables.TryGetValue(name, out var t) ? t : null; + } + + public void AddDataTable(DefTable table, List mainRecords, List patchRecords) + { + _recordsByTables[table.FullName] = new TableDataInfo(mainRecords, patchRecords); + } + + public List GetTableAllDataList(DefTable table) + { + return _recordsByTables[table.FullName].FinalRecords; + } + + public List GetTableExportDataList(DefTable table) + { + var tableDataInfo = _recordsByTables[table.FullName]; + if (_excludeTags.Count == 0) + { + return tableDataInfo.FinalRecords; + } + else + { + var finalRecords = tableDataInfo.FinalRecords.Where(r => r.IsNotFiltered(_excludeTags)).ToList(); + if (table.IsOneValueTable && finalRecords.Count != 1) + { + throw new Exception($"配置表 {table.FullName} 是单值表 mode=one,但数据个数:{finalRecords.Count} != 1"); + } + return finalRecords; + } + } + + public TableDataInfo GetTableDataInfo(DefTable table) + { + return _recordsByTables[table.FullName]; + } + + public List GetExportTables() + { + return Types.Values.Where(t => t is DefTable ct && ct.NeedExport).Select(t => (DefTable)t).ToList(); + } + + public List GetExportTypes() + { + var refTypes = new Dictionary(); + var targetService = CfgTargetService; + foreach (var refType in targetService.Refs) + { + if (!this.Types.ContainsKey(refType)) + { + throw new Exception($"service:'{targetService.Name}' ref:'{refType}' 类型不存在"); + } + if (!refTypes.TryAdd(refType, this.Types[refType])) + { + throw new Exception($"service:'{targetService.Name}' ref:'{refType}' 重复引用"); + } + } + foreach (var e in this.Types) + { + if (!refTypes.ContainsKey(e.Key) && (e.Value is DefEnum)) + { + refTypes.Add(e.Key, e.Value); + } + } + + foreach (var table in GetExportTables()) + { + refTypes[table.FullName] = table; + table.ValueTType.Apply(RefTypeVisitor.Ins, refTypes); + } + + return refTypes.Values.ToList(); + } + + public void Load(string outputService, Defines defines) + { + SupportDatetimeType = true; + + TopModule = defines.TopModule; + + CfgTargetService = defines.Services.Find(s => s.Name == outputService); + + if (CfgTargetService == null) + { + throw new ArgumentException($"service:{outputService} not exists"); + } + + if (!string.IsNullOrWhiteSpace(_patchName)) + { + TargetPatch = defines.Patches.Find(b => b.Name == _patchName); + if (TargetPatch == null) + { + throw new Exception($"patch '{_patchName}' not in valid patch set"); + } + } + + this._patches.AddRange(defines.Patches); + + foreach (var e in defines.Enums) + { + AddType(new DefEnum(e)); + } + + foreach (var b in defines.Beans) + { + AddType(new DefBean((CfgBean)b)); + } + + foreach (var p in defines.Tables) + { + var table = new DefTable(p); + AddType(table); + AddCfgTable(table); + } + + _cfgServices.AddRange(defines.Services); + + foreach (var type in Types.Values) + { + type.AssemblyBase = this; + } + + foreach (var type in Types.Values) + { + try + { + s_logger.Trace("precompile type:{0} begin", type.FullName); + type.PreCompile(); + s_logger.Trace("precompile type:{0} end", type.FullName); + } + catch (Exception) + { + this.Agent.Error("precompile type:{0} error", type.FullName); + throw; + } + } + foreach (var type in Types.Values) + { + try + { + s_logger.Trace("compile type:{0} begin", type.FullName); + type.Compile(); + s_logger.Trace("compile type:{0} end", type.FullName); + } + catch (Exception) + { + this.Agent.Error("compile type:{0} error", type.FullName); + s_logger.Error("compile type:{0} error", type.FullName); + throw; + } + } + foreach (var type in Types.Values) + { + try + { + s_logger.Trace("post compile type:{0} begin", type.FullName); + type.PostCompile(); + s_logger.Trace("post compile type:{0} end", type.FullName); + } + catch (Exception) + { + this.Agent.Error("post compile type:{0} error", type.FullName); + s_logger.Error("post compile type:{0} error", type.FullName); + throw; + } + } + + // 丑陋. 怎么写更好? + + // 递归 设置DefBean及DefField 的 IsMultiRow + + MarkMultiRows(); + } + + public void MarkMultiRows() + { + var multiRowBeans = new HashSet(); + for (bool anyMark = true; anyMark;) + { + anyMark = false; + foreach (var type in this.Types.Values) + { + if (type is DefBean beanType && !beanType.IsMultiRow) + { + bool isMultiRows; + if (beanType.IsNotAbstractType) + { + isMultiRows = beanType.HierarchyFields.Any(f => ((DefField)f).ComputeIsMultiRow()); + } + else + { + isMultiRows = beanType.HierarchyNotAbstractChildren.Any(c => ((DefBean)c).IsMultiRow); + } + if (isMultiRows) + { + beanType.IsMultiRow = true; + //s_logger.Info("bean:{bean} is multi row", beanType.FullName); + anyMark = true; + } + } + } + + } + } + } +} diff --git a/src/LubanAssistant/Source/Defs/DefField.cs b/src/LubanAssistant/Source/Defs/DefField.cs new file mode 100644 index 0000000..cccacb5 --- /dev/null +++ b/src/LubanAssistant/Source/Defs/DefField.cs @@ -0,0 +1,181 @@ +using Luban.Common.Utils; +using Luban.Job.Cfg.DataCreators; +using Luban.Job.Cfg.Datas; +using Luban.Job.Cfg.RawDefs; +using Luban.Job.Common.Defs; +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Luban.Job.Cfg.Defs +{ + public class DefField : DefFieldBase + { + public DefAssembly Assembly => (DefAssembly)HostType.AssemblyBase; + + + public bool RawIsMultiRow { get; } + + public bool IsMultiRow { get; private set; } + + public bool ComputeIsMultiRow() + { + if (IsMultiRow) + { + return true; + } + + switch (CType) + { + case TBean b: { return IsMultiRow = ((DefBean)b.Bean).IsMultiRow; } + case TList b: { return IsMultiRow = b.ElementType is TBean b2 && ((DefBean)b2.Bean).IsMultiRow; } + case TArray b: { return IsMultiRow = b.ElementType is TBean b2 && ((DefBean)b2.Bean).IsMultiRow; } + case TMap b: { return IsMultiRow = b.ValueType is TBean b2 && ((DefBean)b2.Bean).IsMultiRow; } + default: return false; + } + } + + + public string Index { get; } + + public List Groups { get; } + + public DefField IndexField { get; private set; } + + + public string Sep { get; set; } + + // 如果没有指定sep + // 如果是bean,且指定了sep,则使用此值 + // 如果是vectorN,使用 , + public string ActualSep => string.IsNullOrWhiteSpace(Sep) ? (CType is TBean bean ? ((DefBean)bean.Bean).Sep : "") : Sep; + + public bool NeedExport => Assembly.NeedExport(this.Groups); + + public TEnum Remapper { get; private set; } + + public CfgField RawDefine { get; } + + public string GetTextKeyName(string name) => name + TText.L10N_FIELD_SUFFIX; + + public bool GenTextKey => this.CType is TText; + + + public string DefaultValue { get; } + + public DType DefalutDtypeValue { get; private set; } + + public bool IsRowOrient { get; } + + public DefField(DefTypeBase host, CfgField f, int idOffset) : base(host, f, idOffset) + { + Index = f.Index; + Sep = f.Sep; + this.IsMultiRow = this.RawIsMultiRow = f.IsMultiRow; + this.Groups = f.Groups; + this.RawDefine = f; + this.DefaultValue = f.DefaultValue; + this.IsRowOrient = f.IsRowOrient; + } + + public override void Compile() + { + base.Compile(); + + if (!string.IsNullOrWhiteSpace(this.DefaultValue)) + { + this.DefalutDtypeValue = CType.Apply(StringDataCreator.Ins, this.DefaultValue); + } + + switch (CType) + { + case TArray t: + { + if (t.ElementType is TBean e && !e.IsDynamic && e.Bean.HierarchyFields.Count == 0) + { + throw new Exception($"container element type:'{e.Bean.FullName}' can't be empty bean"); + } + if (t.ElementType is TText) + { + throw new Exception($"bean:{HostType.FullName} field:{Name} container element type can't text"); + } + break; + } + case TList t: + { + if (t.ElementType is TBean e && !e.IsDynamic && e.Bean.HierarchyFields.Count == 0) + { + throw new Exception($"container element type:'{e.Bean.FullName}' can't be empty bean"); + } + if (t.ElementType is TText) + { + throw new Exception($"bean:{HostType.FullName} field:{Name} container element type can't text"); + } + break; + } + case TSet t: + { + if (t.ElementType is TText) + { + throw new Exception($"bean:{HostType.FullName} field:{Name} container element type can't text"); + } + break; + } + case TMap t: + { + if (t.KeyType is TText) + { + throw new Exception($"bean:{HostType.FullName} field:{Name} container key type can't text"); + } + if (t.ValueType is TText) + { + throw new Exception($"bean:{HostType.FullName} field:{Name} container value type can't text"); + } + break; + } + } + + if (IsMultiRow && !CType.IsCollection && !CType.IsBean) + { + throw new Exception($"只有容器类型才支持 multi_line 属性"); + } + + if (string.IsNullOrEmpty(Sep) && CType is TBean bean) + { + Sep = bean.GetBeanAs().Sep; + } + if (!string.IsNullOrEmpty(Index)) + { + if ((CType is TArray tarray) && (tarray.ElementType is TBean b)) + { + if ((IndexField = b.GetBeanAs().GetField(Index)) == null) + { + throw new Exception($"type:'{HostType.FullName}' field:'{Name}' index:'{Index}'. index not exist"); + } + } + else if ((CType is TList tlist) && (tlist.ElementType is TBean tb)) + { + if ((IndexField = tb.GetBeanAs().GetField(Index)) == null) + { + throw new Exception($"type:'{HostType.FullName}' field:'{Name}' index:'{Index}'. index not exist"); + } + } + else + { + throw new Exception($"type:'{HostType.FullName}' field:'{Name}' index:'{Index}'. only array:bean or list:bean support index"); + } + } + + if (!string.IsNullOrEmpty(this.RawDefine.Converter)) + { + this.Remapper = AssemblyBase.GetDefTType(HostType.Namespace, this.RawDefine.Converter, this.IsNullable) as TEnum; + if (this.Remapper == null) + { + throw new Exception($"type:'{HostType.FullName}' field:'{Name}' converter:'{this.RawDefine.Converter}' not exists"); + } + } + } + } +} diff --git a/src/LubanAssistant/Source/Defs/DefTypeBase.cs b/src/LubanAssistant/Source/Defs/DefTypeBase.cs new file mode 100644 index 0000000..51d7bcf --- /dev/null +++ b/src/LubanAssistant/Source/Defs/DefTypeBase.cs @@ -0,0 +1,69 @@ +using Luban.Common.Utils; +using Luban.Server.Common; +using System.Collections.Generic; + +namespace Luban.Job.Common.Defs +{ + public abstract class DefTypeBase + { + public DefAssemblyBase AssemblyBase { get; set; } + + public int Id { get; protected set; } + + public string TopModule => AssemblyBase.TopModule; + + public IAgent Agent => AssemblyBase.Agent; + + public string Name { get; set; } + + public string Namespace { get; set; } + + public string FullName => TypeUtil.MakeFullName(Namespace, Name); + + public string NamespaceWithTopModule => TypeUtil.MakeNamespace(AssemblyBase.TopModule, Namespace); + + public string FullNameWithTopModule => TypeUtil.MakeFullName(AssemblyBase.TopModule, FullName); + + public string JavaFullName => TypeUtil.MakeFullName(Namespace, Name); + + public string GoFullName => TypeUtil.MakeGoFullName(Namespace, Name); + + public string GoPkgName => TypeUtil.MakeGoPkgName(Namespace); + + public string CppNamespaceBegin => TypeUtil.MakeCppNamespaceBegin(Namespace); + + public string CppNamespaceEnd => TypeUtil.MakeCppNamespaceEnd(Namespace); + + public string CppFullNameWithTopModule => TypeUtil.MakeCppFullName(AssemblyBase.TopModule, FullName); + + public string TypescriptNamespaceBegin => TypeUtil.MakeTypescriptNamespaceBegin(Namespace); + + public string TypescriptNamespaceEnd => TypeUtil.MakeTypescriptNamespaceEnd(Namespace); + + public string CppFullName => TypeUtil.MakeCppFullName(Namespace, Name); + + public string PyFullName => TypeUtil.MakePyFullName(Namespace, Name); + + public string RustFullName => TypeUtil.MakeRustFullName(Namespace, Name); + + public string Comment { get; protected set; } + + public Dictionary Tags { get; protected set; } + + public bool HasTag(string attrName) + { + return Tags != null && Tags.ContainsKey(attrName); + } + + public string GetTag(string attrName) + { + return Tags != null && Tags.TryGetValue(attrName, out var value) ? value : null; + } + + public virtual void PreCompile() { } + + public abstract void Compile(); + + public virtual void PostCompile() { } + } +} diff --git a/src/LubanAssistant/Source/FileInfo.cs b/src/LubanAssistant/Source/FileInfo.cs new file mode 100644 index 0000000..5493015 --- /dev/null +++ b/src/LubanAssistant/Source/FileInfo.cs @@ -0,0 +1,28 @@ +using Bright.Serialization; + +namespace Luban.Common.Protos +{ + public class FileInfo : BeanBase + { + public string FilePath { get; set; } + + public string MD5 { get; set; } + + public override void Serialize(ByteBuf os) + { + os.WriteString(FilePath); + os.WriteString(MD5); + } + + public override void Deserialize(ByteBuf os) + { + FilePath = os.ReadString(); + MD5 = os.ReadString(); + } + + public override int GetTypeId() + { + throw new System.NotImplementedException(); + } + } +} diff --git a/src/LubanAssistant/Source/GetImportFileOrDirectory.cs b/src/LubanAssistant/Source/GetImportFileOrDirectory.cs new file mode 100644 index 0000000..61f6432 --- /dev/null +++ b/src/LubanAssistant/Source/GetImportFileOrDirectory.cs @@ -0,0 +1,63 @@ +using Bright.Serialization; +using System; +using System.Collections.Generic; + +namespace Luban.Common.Protos +{ + public class GetImportFileOrDirectoryArg : BeanBase + { + public string FileOrDirName { get; set; } + + public List InclusiveSuffixs { get; set; } = new List(); + + public override int GetTypeId() + { + return 0; + } + + public override void Serialize(ByteBuf os) + { + os.WriteString(FileOrDirName); + Bright.Common.SerializationUtil.Serialize(os, InclusiveSuffixs); + } + public override void Deserialize(ByteBuf os) + { + FileOrDirName = os.ReadString(); + Bright.Common.SerializationUtil.Deserialize(os, InclusiveSuffixs); + } + } + + public class GetImportFileOrDirectoryRes : BeanBase + { + public EErrorCode Err { get; set; } + + public bool IsFile { get; set; } + + public string Md5 { get; set; } + + //public byte[] Content { get; set; } + + public List SubFiles { get; set; } + + public override int GetTypeId() + { + return 0; + } + + public override void Serialize(ByteBuf os) + { + os.WriteInt((int)Err); + os.WriteBool(IsFile); + os.WriteString(Md5); + Bright.Common.SerializationUtil.Serialize(os, SubFiles); + } + public override void Deserialize(ByteBuf os) + { + Err = (EErrorCode)os.ReadInt(); + IsFile = os.ReadBool(); + Md5 = os.ReadString(); + Bright.Common.SerializationUtil.Deserialize(os, SubFiles = new List()); + } + } + +} diff --git a/src/LubanAssistant/Source/LocalAgent.cs b/src/LubanAssistant/Source/LocalAgent.cs new file mode 100644 index 0000000..4669d28 --- /dev/null +++ b/src/LubanAssistant/Source/LocalAgent.cs @@ -0,0 +1,122 @@ + +using Bright.Time; +using Luban.Common.Protos; +using Luban.Common.Utils; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Luban.Server.Common +{ + public class LocalAgent : IAgent + { + private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger(); + + public LocalAgent() + { + } + + public Task ReadAllBytesAsync(string file) + { + return FileUtil.ReadAllBytesAsync(file); + } + + public async Task GetFromCacheOrReadAllBytesAsync(string file, string md5) + { + var content = await ReadAllBytesAsync(file).ConfigureAwait(false); + return content; + } + + + public async Task GetFileOrDirectoryAsync(string file, params string[] searchPatterns) + { + long t1 = TimeUtil.NowMillis; + var re = new GetImportFileOrDirectoryRes() + { + SubFiles = new List(), + }; + var suffixes = new List(searchPatterns.Select(s => s.Trim()).Where(s => !string.IsNullOrWhiteSpace(s))); + + try + { + if (Directory.Exists(file)) + { + re.Err = 0; + re.IsFile = false; + foreach (var subFile in Directory.GetFiles(file, "*", SearchOption.AllDirectories)) + { + if (FileUtil.IsValidInputFile(subFile) && (suffixes.Count == 0 || suffixes.Any(s => subFile.EndsWith(s)))) + { + + var md5 = FileUtil.CalcMD5(await FileUtil.ReadAllBytesAsync(subFile)); + re.SubFiles.Add(new Luban.Common.Protos.FileInfo() { FilePath = FileUtil.Standardize(subFile), MD5 = md5 }); + } + } + + } + else if (File.Exists(file)) + { + re.IsFile = true; + re.Md5 = FileUtil.CalcMD5(await FileUtil.ReadAllBytesAsync(file)); + } + else + { + re.Err = Luban.Common.EErrorCode.FILE_OR_DIR_NOT_EXISTS; + } + } + catch (Exception e) + { + re.Err = Luban.Common.EErrorCode.READ_FILE_FAIL; + s_logger.Error(e); + } + + s_logger.Trace(" GetImportFileOrDirectory file:{file} err:{err} cost:{time}", file, re.Err, TimeUtil.NowMillis - t1); + + return re; + } + + public Task QueryFileExistsAsync(QueryFilesExistsArg arg) + { + var re = new QueryFilesExistsRes() { Exists = new List(arg.Files.Count) }; + foreach (var f in arg.Files) + { + re.Exists.Add(File.Exists(Path.Combine(arg.Root, f))); + } + return Task.FromResult(re); + } + + public async Task OpenXmlAsync(string xmlFile) + { + try + { + s_logger.Trace("open {xml}", xmlFile); + return XElement.Load(new MemoryStream(await ReadAllBytesAsync(xmlFile))); + } + catch (Exception e) + { + throw new Exception($"打开定义文件:{xmlFile} 失败 --> {e.Message}"); + } + } + + #region log + + public void Error(string fmt, params object[] objs) + { + Log("error", string.Format(fmt, objs)); + } + + public void Info(string fmt, params object[] objs) + { + Log("info", string.Format(fmt, objs)); + } + + private void Log(string level, string content) + { + //Session.Send(new PushLog() { Level = level, LogContent = content }); + } + #endregion + } +} diff --git a/src/LubanAssistant/Source/QueryFilesExists.cs b/src/LubanAssistant/Source/QueryFilesExists.cs new file mode 100644 index 0000000..e509c2c --- /dev/null +++ b/src/LubanAssistant/Source/QueryFilesExists.cs @@ -0,0 +1,58 @@ +using Bright.Serialization; +using System; +using System.Collections.Generic; + +namespace Luban.Common.Protos +{ + public class QueryFilesExistsArg : BeanBase + { + public string Root { get; set; } + + public List Files { get; set; } + + public override int GetTypeId() + { + return 0; + } + + public override void Serialize(ByteBuf os) + { + os.WriteString(Root); + Bright.Common.SerializationUtil.Serialize(os, Files); + } + public override void Deserialize(ByteBuf os) + { + Root = os.ReadString(); + Bright.Common.SerializationUtil.Deserialize(os, Files = new List()); + } + } + + public class QueryFilesExistsRes : BeanBase + { + public List Exists { get; set; } + + public override int GetTypeId() + { + return 0; + } + + public override void Serialize(ByteBuf os) + { + os.WriteSize(Exists.Count); + foreach (var v in Exists) + { + os.WriteBool(v); + } + } + public override void Deserialize(ByteBuf os) + { + int n = os.ReadSize(); + Exists = new List(); + for (int i = 0; i < n; i++) + { + Exists.Add(os.ReadBool()); + } + } + } + +} diff --git a/src/LubanAssistant/Source/Serialization/BeanBase.cs b/src/LubanAssistant/Source/Serialization/BeanBase.cs new file mode 100644 index 0000000..6d56504 --- /dev/null +++ b/src/LubanAssistant/Source/Serialization/BeanBase.cs @@ -0,0 +1,11 @@ +namespace Bright.Serialization +{ + public abstract class BeanBase : ITypeId, ISerializable + { + public abstract int GetTypeId(); + + public abstract void Serialize(ByteBuf os); + + public abstract void Deserialize(ByteBuf os); + } +} diff --git a/src/LubanAssistant/Source/Serialization/ByteBuf.cs b/src/LubanAssistant/Source/Serialization/ByteBuf.cs new file mode 100644 index 0000000..5def2a5 --- /dev/null +++ b/src/LubanAssistant/Source/Serialization/ByteBuf.cs @@ -0,0 +1,1687 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Dynamic; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Text; + + +/// +/// TODO +/// 1. 整理代码 +/// 2. 优化序列化 (像这样 data[endPos + 1] = (byte)(x >> 8) 挨个字节赋值总感觉很低效,能优化吗) +/// + + +namespace Bright.Serialization +{ + + public readonly struct SegmentSaveState + { + public SegmentSaveState(int readerIndex, int writerIndex) + { + ReaderIndex = readerIndex; + WriterIndex = writerIndex; + } + + public int ReaderIndex { get; } + + public int WriterIndex { get; } + } + + public sealed class ByteBuf : ICloneable, IEquatable + { + public ByteBuf() + { + Bytes = Array.Empty(); + ReaderIndex = WriterIndex = 0; + } + + public ByteBuf(int capacity) + { + Bytes = capacity > 0 ? new byte[capacity] : Array.Empty(); + ReaderIndex = 0; + WriterIndex = 0; + } + + public ByteBuf(byte[] bytes) + { + Bytes = bytes; + ReaderIndex = 0; + WriterIndex = Capacity; + } + + public ByteBuf(byte[] bytes, int readIndex, int writeIndex) + { + Bytes = bytes; + ReaderIndex = readIndex; + WriterIndex = writeIndex; + } + + public ByteBuf(int capacity, Action releaser) : this(capacity) + { + _releaser = releaser; + } + + public static ByteBuf Wrap(byte[] bytes) + { + return new ByteBuf(bytes, 0, bytes.Length); + } + + public void Replace(byte[] bytes) + { + Bytes = bytes; + ReaderIndex = 0; + WriterIndex = Capacity; + } + + public void Replace(byte[] bytes, int beginPos, int endPos) + { + Bytes = bytes; + ReaderIndex = beginPos; + WriterIndex = endPos; + } + + public int ReaderIndex { get; set; } + + public int WriterIndex { get; set; } + + private readonly Action _releaser; + + public int Capacity => Bytes.Length; + + public int Size { get { return WriterIndex - ReaderIndex; } } + + public bool Empty => WriterIndex <= ReaderIndex; + + public bool NotEmpty => WriterIndex > ReaderIndex; + + + public void AddWriteIndex(int add) + { + WriterIndex += add; + } + + public void AddReadIndex(int add) + { + ReaderIndex += add; + } + +#pragma warning disable CA1819 // 属性不应返回数组 + public byte[] Bytes { get; private set; } +#pragma warning restore CA1819 // 属性不应返回数组 + + public byte[] CopyData() + { + var n = Remaining; + if (n > 0) + { + var arr = new byte[n]; + Buffer.BlockCopy(Bytes, ReaderIndex, arr, 0, n); + return arr; + } + else + { + return Array.Empty(); + } + } + + public int Remaining { get { return WriterIndex - ReaderIndex; } } + + public void DiscardReadBytes() + { + WriterIndex -= ReaderIndex; + Array.Copy(Bytes, ReaderIndex, Bytes, 0, WriterIndex); + ReaderIndex = 0; + } + + public int NotCompactWritable { get { return Capacity - WriterIndex; } } + + public void WriteBytesWithoutSize(byte[] bs) + { + WriteBytesWithoutSize(bs, 0, bs.Length); + } + + public void WriteBytesWithoutSize(byte[] bs, int offset, int len) + { + EnsureWrite(len); + Buffer.BlockCopy(bs, offset, Bytes, WriterIndex, len); + WriterIndex += len; + } + + public void Clear() + { + ReaderIndex = WriterIndex = 0; + } + + private const int MIN_CAPACITY = 16; + + private static int PropSize(int initSize, int needSize) + { + for (int i = Math.Max(initSize, MIN_CAPACITY); ; i <<= 1) + { + if (i >= needSize) + { + return i; + } + } + } + + private void EnsureWrite0(int size) + { + var needSize = WriterIndex + size - ReaderIndex; + if (needSize < Capacity) + { + WriterIndex -= ReaderIndex; + Array.Copy(Bytes, ReaderIndex, Bytes, 0, WriterIndex); + ReaderIndex = 0; + } + else + { + int newCapacity = PropSize(Capacity, needSize); + var newBytes = new byte[newCapacity]; + WriterIndex -= ReaderIndex; + Buffer.BlockCopy(Bytes, ReaderIndex, newBytes, 0, WriterIndex); + ReaderIndex = 0; + Bytes = newBytes; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void EnsureWrite(int size) + { + if (WriterIndex + size > Capacity) + { + EnsureWrite0(size); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void EnsureRead(int size) + { + if (ReaderIndex + size > WriterIndex) + { + throw new SerializationException(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool CanRead(int size) + { + return (ReaderIndex + size <= WriterIndex); + } + + public void Append(byte x) + { + EnsureWrite(1); + Bytes[WriterIndex++] = x; + } + + public void WriteBool(bool b) + { + EnsureWrite(1); + Bytes[WriterIndex++] = (byte)(b ? 1 : 0); + } + + public bool ReadBool() + { + EnsureRead(1); + return Bytes[ReaderIndex++] != 0; + } + + public void WriteByte(byte x) + { + EnsureWrite(1); + Bytes[WriterIndex++] = x; + } + + public byte ReadByte() + { + EnsureRead(1); + return Bytes[ReaderIndex++]; + } + + + public void WriteShort(short x) + { + if (x >= 0) + { + if (x < 0x80) + { + EnsureWrite(1); + Bytes[WriterIndex++] = (byte)x; + return; + } + else if (x < 0x4000) + { + EnsureWrite(2); + Bytes[WriterIndex + 1] = (byte)x; + Bytes[WriterIndex] = (byte)((x >> 8) | 0x80); + WriterIndex += 2; + return; + } + } + EnsureWrite(3); + Bytes[WriterIndex] = 0xff; + Bytes[WriterIndex + 2] = (byte)x; + Bytes[WriterIndex + 1] = (byte)(x >> 8); + WriterIndex += 3; + } + + public short ReadShort() + { + EnsureRead(1); + int h = Bytes[ReaderIndex]; + if (h < 0x80) + { + ReaderIndex++; + return (short)h; + } + else if (h < 0xc0) + { + EnsureRead(2); + int x = ((h & 0x3f) << 8) | Bytes[ReaderIndex + 1]; + ReaderIndex += 2; + return (short)x; + } + else if ((h == 0xff)) + { + EnsureRead(3); + int x = (Bytes[ReaderIndex + 1] << 8) | Bytes[ReaderIndex + 2]; + ReaderIndex += 3; + return (short)x; + } + else + { + throw new SerializationException(); + } + } + + public short ReadFshort() + { + EnsureRead(2); + short x; +#if CPU_SUPPORT_MEMORY_NOT_ALIGN + unsafe + { + fixed (byte* b = &Bytes[ReaderIndex]) + { + x = *(short*)b; + } + } +#else + x = (short)((Bytes[ReaderIndex + 1] << 8) | Bytes[ReaderIndex]); + +#endif + ReaderIndex += 2; + return x; + } + + public void WriteFshort(short x) + { + EnsureWrite(2); +#if CPU_SUPPORT_MEMORY_NOT_ALIGN + unsafe + { + fixed (byte* b = &Bytes[WriterIndex]) + { + *(short*)b = x; + } + } +#else + Bytes[WriterIndex] = (byte)x; + Bytes[WriterIndex + 1] = (byte)(x >> 8); +#endif + WriterIndex += 2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteInt(int x) + { + WriteUint((uint)x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ReadInt() + { + return (int)ReadUint(); + } + + + public void WriteUint(uint x) + { + // 如果有修改,记得也把 EndWriteSegment改了 + // 0 111 1111 + if (x < 0x80) + { + EnsureWrite(1); + Bytes[WriterIndex++] = (byte)x; + } + else if (x < 0x4000) // 10 11 1111, - + { + EnsureWrite(2); + Bytes[WriterIndex + 1] = (byte)x; + Bytes[WriterIndex] = (byte)((x >> 8) | 0x80); + WriterIndex += 2; + } + else if (x < 0x200000) // 110 1 1111, -,- + { + EnsureWrite(3); + Bytes[WriterIndex + 2] = (byte)x; + Bytes[WriterIndex + 1] = (byte)(x >> 8); + Bytes[WriterIndex] = (byte)((x >> 16) | 0xc0); + WriterIndex += 3; + } + else if (x < 0x10000000) // 1110 1111,-,-,- + { + EnsureWrite(4); + Bytes[WriterIndex + 3] = (byte)x; + Bytes[WriterIndex + 2] = (byte)(x >> 8); + Bytes[WriterIndex + 1] = (byte)(x >> 16); + Bytes[WriterIndex] = (byte)((x >> 24) | 0xe0); + WriterIndex += 4; + } + else + { + EnsureWrite(5); + Bytes[WriterIndex] = 0xf0; + Bytes[WriterIndex + 4] = (byte)x; + Bytes[WriterIndex + 3] = (byte)(x >> 8); + Bytes[WriterIndex + 2] = (byte)(x >> 16); + Bytes[WriterIndex + 1] = (byte)(x >> 24); + WriterIndex += 5; + } + } + + public uint ReadUint() + { + /// + /// 警告! 如有修改,记得调整 TryDeserializeInplaceOctets + EnsureRead(1); + uint h = Bytes[ReaderIndex]; + if (h < 0x80) + { + ReaderIndex++; + return h; + } + else if (h < 0xc0) + { + EnsureRead(2); + uint x = ((h & 0x3f) << 8) | Bytes[ReaderIndex + 1]; + ReaderIndex += 2; + return x; + } + else if (h < 0xe0) + { + EnsureRead(3); + uint x = ((h & 0x1f) << 16) | ((uint)Bytes[ReaderIndex + 1] << 8) | Bytes[ReaderIndex + 2]; + ReaderIndex += 3; + return x; + } + else if (h < 0xf0) + { + + EnsureRead(4); + uint x = ((h & 0x0f) << 24) | ((uint)Bytes[ReaderIndex + 1] << 16) | ((uint)Bytes[ReaderIndex + 2] << 8) | Bytes[ReaderIndex + 3]; + ReaderIndex += 4; + return x; + } + else + { + EnsureRead(5); + uint x = ((uint)Bytes[ReaderIndex + 1] << 24) | ((uint)(Bytes[ReaderIndex + 2] << 16)) | ((uint)Bytes[ReaderIndex + 3] << 8) | Bytes[ReaderIndex + 4]; + ReaderIndex += 5; + return x; + } + } + + public unsafe void WriteUint_Unsafe(uint x) + { + // 0 111 1111 + if (x < 0x80) + { + EnsureWrite(1); + Bytes[WriterIndex++] = (byte)(x << 1); + } + else if (x < 0x4000)// 10 11 1111, - + { + EnsureWrite(2); + + fixed (byte* wb = &Bytes[WriterIndex]) + { + *(uint*)(wb) = (x << 2 | 0b01); + } + + WriterIndex += 2; + } + else if (x < 0x200000) // 110 1 1111, -,- + { + EnsureWrite(3); + + fixed (byte* wb = &Bytes[WriterIndex]) + { + *(uint*)(wb) = (x << 3 | 0b011); + } + WriterIndex += 3; + } + else if (x < 0x10000000) // 1110 1111,-,-,- + { + EnsureWrite(4); + fixed (byte* wb = &Bytes[WriterIndex]) + { + *(uint*)(wb) = (x << 4 | 0b0111); + } + WriterIndex += 4; + } + else + { + EnsureWrite(5); + fixed (byte* wb = &Bytes[WriterIndex]) + { + *(uint*)(wb) = (x << 5 | 0b01111); + } + WriterIndex += 5; + } + } + + public unsafe uint ReadUint_Unsafe() + { + /// + /// 警告! 如有修改,记得调整 TryDeserializeInplaceOctets + EnsureRead(1); + uint h = Bytes[ReaderIndex]; + if ((h & 0b1) == 0b0) + { + ReaderIndex++; + return (h >> 1); + } + else if ((h & 0b11) == 0b01) + { + EnsureRead(2); + fixed (byte* rb = &Bytes[ReaderIndex]) + { + ReaderIndex += 2; + return (*(uint*)rb) >> 2; + } + } + else if ((h & 0b111) == 0b011) + { + EnsureRead(3); + fixed (byte* rb = &Bytes[ReaderIndex]) + { + ReaderIndex += 3; + return (*(uint*)rb) >> 3; + } + } + else if ((h & 0b1111) == 0b0111) + { + EnsureRead(4); + fixed (byte* rb = &Bytes[ReaderIndex]) + { + ReaderIndex += 4; + return (*(uint*)rb) >> 4; + } + } + else + { + EnsureRead(5); + fixed (byte* rb = &Bytes[ReaderIndex]) + { + ReaderIndex += 5; + return (*(uint*)rb) >> 5; + } + } + } + + public int ReadFint() + { + EnsureRead(4); + int x; +#if CPU_SUPPORT_MEMORY_NOT_ALIGN + unsafe + { + fixed (byte* b = &Bytes[ReaderIndex]) + { + x = *(int*)b; + } + } +#else + x = (Bytes[ReaderIndex + 3] << 24) | (Bytes[ReaderIndex + 2] << 16) | (Bytes[ReaderIndex + 1] << 8) | (Bytes[ReaderIndex]); + +#endif + ReaderIndex += 4; + return x; + } + + + public void WriteFint(int x) + { + EnsureWrite(4); +#if CPU_SUPPORT_MEMORY_NOT_ALIGN + unsafe + { + fixed (byte* b = &Bytes[WriterIndex]) + { + *(int*)b = x; + } + } +#else + Bytes[WriterIndex] = (byte)x; + Bytes[WriterIndex + 1] = (byte)(x >> 8); + Bytes[WriterIndex + 2] = (byte)(x >> 16); + Bytes[WriterIndex + 3] = (byte)(x >> 24); +#endif + WriterIndex += 4; + } + + public int ReadFint_Safe() + { + EnsureRead(4); + int x; + + x = (Bytes[ReaderIndex + 3] << 24) | (Bytes[ReaderIndex + 2] << 16) | (Bytes[ReaderIndex + 1] << 8) | (Bytes[ReaderIndex]); + + ReaderIndex += 4; + return x; + } + + + public void WriteFint_Safe(int x) + { + EnsureWrite(4); + Bytes[WriterIndex] = (byte)x; + Bytes[WriterIndex + 1] = (byte)(x >> 8); + Bytes[WriterIndex + 2] = (byte)(x >> 16); + Bytes[WriterIndex + 3] = (byte)(x >> 24); + WriterIndex += 4; + } + + public void WriteLong(long x) + { + WriteUlong((ulong)x); + } + + public long ReadLong() + { + return (long)ReadUlong(); + } + + public void WriteNumberAsLong(double x) + { + WriteLong((long)x); + } + + public double ReadLongAsNumber() + { + return ReadLong(); + } + + private void WriteUlong(ulong x) + { + // 0 111 1111 + if (x < 0x80) + { + EnsureWrite(1); + Bytes[WriterIndex++] = (byte)x; + } + else if (x < 0x4000) // 10 11 1111, - + { + EnsureWrite(2); + Bytes[WriterIndex + 1] = (byte)x; + Bytes[WriterIndex] = (byte)((x >> 8) | 0x80); + WriterIndex += 2; + } + else if (x < 0x200000) // 110 1 1111, -,- + { + EnsureWrite(3); + Bytes[WriterIndex + 2] = (byte)x; + Bytes[WriterIndex + 1] = (byte)(x >> 8); + Bytes[WriterIndex] = (byte)((x >> 16) | 0xc0); + WriterIndex += 3; + } + else if (x < 0x10000000) // 1110 1111,-,-,- + { + EnsureWrite(4); + Bytes[WriterIndex + 3] = (byte)x; + Bytes[WriterIndex + 2] = (byte)(x >> 8); + Bytes[WriterIndex + 1] = (byte)(x >> 16); + Bytes[WriterIndex] = (byte)((x >> 24) | 0xe0); + WriterIndex += 4; + } + else if (x < 0x800000000L) // 1111 0xxx,-,-,-,- + { + EnsureWrite(5); + Bytes[WriterIndex + 4] = (byte)x; + Bytes[WriterIndex + 3] = (byte)(x >> 8); + Bytes[WriterIndex + 2] = (byte)(x >> 16); + Bytes[WriterIndex + 1] = (byte)(x >> 24); + Bytes[WriterIndex] = (byte)((x >> 32) | 0xf0); + WriterIndex += 5; + } + else if (x < 0x40000000000L) // 1111 10xx, + { + EnsureWrite(6); + Bytes[WriterIndex + 5] = (byte)x; + Bytes[WriterIndex + 4] = (byte)(x >> 8); + Bytes[WriterIndex + 3] = (byte)(x >> 16); + Bytes[WriterIndex + 2] = (byte)(x >> 24); + Bytes[WriterIndex + 1] = (byte)(x >> 32); + Bytes[WriterIndex] = (byte)((x >> 40) | 0xf8); + WriterIndex += 6; + } + else if (x < 0x200000000000L) // 1111 110x, + { + EnsureWrite(7); + Bytes[WriterIndex + 6] = (byte)x; + Bytes[WriterIndex + 5] = (byte)(x >> 8); + Bytes[WriterIndex + 4] = (byte)(x >> 16); + Bytes[WriterIndex + 3] = (byte)(x >> 24); + Bytes[WriterIndex + 2] = (byte)(x >> 32); + Bytes[WriterIndex + 1] = (byte)(x >> 40); + Bytes[WriterIndex] = (byte)((x >> 48) | 0xfc); + WriterIndex += 7; + } + else if (x < 0x100000000000000L) // 1111 1110 + { + EnsureWrite(8); + Bytes[WriterIndex + 7] = (byte)x; + Bytes[WriterIndex + 6] = (byte)(x >> 8); + Bytes[WriterIndex + 5] = (byte)(x >> 16); + Bytes[WriterIndex + 4] = (byte)(x >> 24); + Bytes[WriterIndex + 3] = (byte)(x >> 32); + Bytes[WriterIndex + 2] = (byte)(x >> 40); + Bytes[WriterIndex + 1] = (byte)(x >> 48); + Bytes[WriterIndex] = 0xfe; + WriterIndex += 8; + } + else // 1111 1111 + { + EnsureWrite(9); + Bytes[WriterIndex] = 0xff; + Bytes[WriterIndex + 8] = (byte)x; + Bytes[WriterIndex + 7] = (byte)(x >> 8); + Bytes[WriterIndex + 6] = (byte)(x >> 16); + Bytes[WriterIndex + 5] = (byte)(x >> 24); + Bytes[WriterIndex + 4] = (byte)(x >> 32); + Bytes[WriterIndex + 3] = (byte)(x >> 40); + Bytes[WriterIndex + 2] = (byte)(x >> 48); + Bytes[WriterIndex + 1] = (byte)(x >> 56); + WriterIndex += 9; + } + } + + public ulong ReadUlong() + { + EnsureRead(1); + uint h = Bytes[ReaderIndex]; + if (h < 0x80) + { + ReaderIndex++; + return h; + } + else if (h < 0xc0) + { + EnsureRead(2); + uint x = ((h & 0x3f) << 8) | Bytes[ReaderIndex + 1]; + ReaderIndex += 2; + return x; + } + else if (h < 0xe0) + { + EnsureRead(3); + uint x = ((h & 0x1f) << 16) | ((uint)Bytes[ReaderIndex + 1] << 8) | Bytes[ReaderIndex + 2]; + ReaderIndex += 3; + return x; + } + else if (h < 0xf0) + { + EnsureRead(4); + uint x = ((h & 0x0f) << 24) | ((uint)Bytes[ReaderIndex + 1] << 16) | ((uint)Bytes[ReaderIndex + 2] << 8) | Bytes[ReaderIndex + 3]; + ReaderIndex += 4; + return x; + } + else if (h < 0xf8) + { + EnsureRead(5); + uint xl = ((uint)Bytes[ReaderIndex + 1] << 24) | ((uint)(Bytes[ReaderIndex + 2] << 16)) | ((uint)Bytes[ReaderIndex + 3] << 8) | (Bytes[ReaderIndex + 4]); + uint xh = h & 0x07; + ReaderIndex += 5; + return ((ulong)xh << 32) | xl; + } + else if (h < 0xfc) + { + EnsureRead(6); + uint xl = ((uint)Bytes[ReaderIndex + 2] << 24) | ((uint)(Bytes[ReaderIndex + 3] << 16)) | ((uint)Bytes[ReaderIndex + 4] << 8) | (Bytes[ReaderIndex + 5]); + uint xh = ((h & 0x03) << 8) | Bytes[ReaderIndex + 1]; + ReaderIndex += 6; + return ((ulong)xh << 32) | xl; + } + else if (h < 0xfe) + { + EnsureRead(7); + uint xl = ((uint)Bytes[ReaderIndex + 3] << 24) | ((uint)(Bytes[ReaderIndex + 4] << 16)) | ((uint)Bytes[ReaderIndex + 5] << 8) | (Bytes[ReaderIndex + 6]); + uint xh = ((h & 0x01) << 16) | ((uint)Bytes[ReaderIndex + 1] << 8) | Bytes[ReaderIndex + 2]; + ReaderIndex += 7; + return ((ulong)xh << 32) | xl; + } + else if (h < 0xff) + { + EnsureRead(8); + uint xl = ((uint)Bytes[ReaderIndex + 4] << 24) | ((uint)(Bytes[ReaderIndex + 5] << 16)) | ((uint)Bytes[ReaderIndex + 6] << 8) | (Bytes[ReaderIndex + 7]); + uint xh = /*((h & 0x01) << 24) |*/ ((uint)Bytes[ReaderIndex + 1] << 16) | ((uint)Bytes[ReaderIndex + 2] << 8) | Bytes[ReaderIndex + 3]; + ReaderIndex += 8; + return ((ulong)xh << 32) | xl; + } + else + { + EnsureRead(9); + uint xl = ((uint)Bytes[ReaderIndex + 5] << 24) | ((uint)(Bytes[ReaderIndex + 6] << 16)) | ((uint)Bytes[ReaderIndex + 7] << 8) | (Bytes[ReaderIndex + 8]); + uint xh = ((uint)Bytes[ReaderIndex + 1] << 24) | ((uint)Bytes[ReaderIndex + 2] << 16) | ((uint)Bytes[ReaderIndex + 3] << 8) | Bytes[ReaderIndex + 4]; + ReaderIndex += 9; + return ((ulong)xh << 32) | xl; + } + } + + + public void WriteFlong(long x) + { + EnsureWrite(8); +#if CPU_SUPPORT_MEMORY_NOT_ALIGN + unsafe + { + fixed (byte* b = &Bytes[WriterIndex]) + { + *(long*)b = x; + } + } +#else + + Bytes[WriterIndex] = (byte)x; + Bytes[WriterIndex + 1] = (byte)(x >> 8); + Bytes[WriterIndex + 2] = (byte)(x >> 16); + Bytes[WriterIndex + 3] = (byte)(x >> 24); + Bytes[WriterIndex + 4] = (byte)(x >> 32); + Bytes[WriterIndex + 5] = (byte)(x >> 40); + Bytes[WriterIndex + 6] = (byte)(x >> 48); + Bytes[WriterIndex + 7] = (byte)(x >> 56); +#endif + WriterIndex += 8; + } + + public long ReadFlong() + { + EnsureRead(8); + long x; +#if CPU_SUPPORT_MEMORY_NOT_ALIGN + unsafe + { + fixed (byte* b = &Bytes[ReaderIndex]) + { + x = *(long*)b; + } + } +#else + int xl = (Bytes[ReaderIndex + 3] << 24) | ((Bytes[ReaderIndex + 2] << 16)) | (Bytes[ReaderIndex + 1] << 8) | (Bytes[ReaderIndex]); + int xh = (Bytes[ReaderIndex + 7] << 24) | (Bytes[ReaderIndex + 6] << 16) | (Bytes[ReaderIndex + 5] << 8) | Bytes[ReaderIndex + 4]; + x = ((long)xh << 32) | (long)xl; +#endif + ReaderIndex += 8; + return x; + } + + private static unsafe void Copy8(byte* dst, byte* src) + { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + dst[4] = src[4]; + dst[5] = src[5]; + dst[6] = src[6]; + dst[7] = src[7]; + } + + private static unsafe void Copy4(byte* dst, byte* src) + { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + } + + + //const bool isLittleEndian = true; + public void WriteFloat(float x) + { + EnsureWrite(4); + unsafe + { + fixed (byte* b = &Bytes[WriterIndex]) + { +#if !CPU_SUPPORT_MEMORY_NOT_ALIGN + if ((long)b % 4 == 0) + { + *(float*)b = x; + } + else + { + Copy4(b, (byte*)&x); + } +#else + *(float*)b = x; +#endif + } + } + + //if (!BitConverter.IsLittleEndian) + //{ + // Array.Reverse(data, endPos, 4); + //} + WriterIndex += 4; + } + + public float ReadFloat() + { + EnsureRead(4); + //if (!BitConverter.IsLittleEndian) + //{ + // Array.Reverse(data, beginPos, 4); + //} + float x; + unsafe + { + fixed (byte* b = &Bytes[ReaderIndex]) + { +#if !CPU_SUPPORT_MEMORY_NOT_ALIGN + if ((long)b % 4 == 0) + { + x = *(float*)b; + } + else + { + *((int*)&x) = (b[0]) | (b[1] << 8) | (b[2] << 16) | (b[3] << 24); + } +#else + x = *(float*)b; +#endif + } + } + + ReaderIndex += 4; + return x; + } + + public void WriteDouble(double x) + { + EnsureWrite(8); + unsafe + { + fixed (byte* b = &Bytes[WriterIndex]) + { +#if !CPU_SUPPORT_MEMORY_NOT_ALIGN + if ((long)b % 8 == 0) + { + *(double*)b = x; + } + else + { + Copy8(b, (byte*)&x); + } +#else + *(double*)b = x; +#endif + } + //if (!BitConverter.IsLittleEndian) + //{ + // Array.Reverse(data, endPos, 8); + //} + } + + WriterIndex += 8; + } + + public double ReadDouble() + { + EnsureRead(8); + //if (!BitConverter.IsLittleEndian) + //{ + // Array.Reverse(data, beginPos, 8); + //} + double x; + unsafe + { + fixed (byte* b = &Bytes[ReaderIndex]) + { +#if !CPU_SUPPORT_MEMORY_NOT_ALIGN + if ((long)b % 8 == 0) + { + x = *(double*)b; + } + else + { + int low = (b[0]) | (b[1] << 8) | (b[2] << 16) | (b[3] << 24); + int high = (b[4]) | (b[5] << 8) | (b[6] << 16) | (b[7] << 24); + *((long*)&x) = ((long)high << 32) | (uint)low; + } +#else + x = *(double*)b; +#endif + } + } + + ReaderIndex += 8; + return x; + } + + public void WriteSize(int n) + { + WriteUint((uint)n); + } + + public int ReadSize() + { + return (int)ReadUint(); + } + + // marshal int + // n -> (n << 1) ^ (n >> 31) + // Read + // (x >>> 1) ^ ((x << 31) >> 31) + // (x >>> 1) ^ -(n&1) + public void WriteSint(int x) + { + WriteUint(((uint)x << 1) ^ ((uint)x >> 31)); + } + + public int ReadSint() + { + uint x = ReadUint(); + return (int)((x >> 1) ^ ((x & 1) << 31)); + } + + + // marshal long + // n -> (n << 1) ^ (n >> 63) + // Read + // (x >>> 1) ^((x << 63) >> 63) + // (x >>> 1) ^ -(n&1L) + public void WriteSlong(long x) + { + WriteUlong(((ulong)x << 1) ^ ((ulong)x >> 63)); + } + + public long ReadSlong() + { + long x = ReadLong(); + return ((long)((ulong)x >> 1) ^ ((x & 1) << 63)); + } + + public void WriteString(string x) + { + var n = x != null ? Encoding.UTF8.GetByteCount(x) : 0; + WriteSize(n); + if (n > 0) + { + EnsureWrite(n); + Encoding.UTF8.GetBytes(x, 0, x.Length, Bytes, WriterIndex); + WriterIndex += n; + } + } + + // byte[], [start, end) + public static Func, string> StringCacheFinder { get; set; } + + public string ReadString() + { + var n = ReadSize(); + if (n > 0) + { + EnsureRead(n); + string s; + + if (StringCacheFinder == null) + { + s = Encoding.UTF8.GetString(Bytes, ReaderIndex, n); + } + else + { + // 只缓存比较小的字符串 + s = StringCacheFinder(new ReadOnlyMemory(Bytes, ReaderIndex, n)); + } + ReaderIndex += n; + return s; + } + else + { + return string.Empty; + } + } + + public void WriteBytes(byte[] x) + { + var n = x != null ? x.Length : 0; + WriteSize(n); + if (n > 0) + { + EnsureWrite(n); + x.CopyTo(Bytes, WriterIndex); + WriterIndex += n; + } + } + + public byte[] ReadBytes() + { + var n = ReadSize(); + if (n > 0) + { + EnsureRead(n); + var x = new byte[n]; + Buffer.BlockCopy(Bytes, ReaderIndex, x, 0, n); + ReaderIndex += n; + return x; + } + else + { + return Array.Empty(); + } + } + + // 以下是一些特殊类型 + + public void WriteComplex(Complex x) + { + WriteDouble(x.Real); + WriteDouble(x.Imaginary); + } + + public Complex ReadComplex() + { + var x = ReadDouble(); + var y = ReadDouble(); + return new Complex(x, y); + } + + public void WriteVector2(Vector2 x) + { + WriteFloat(x.X); + WriteFloat(x.Y); + } + + public Vector2 ReadVector2() + { + float x = ReadFloat(); + float y = ReadFloat(); + return new Vector2(x, y); + } + + public void WriteVector3(Vector3 x) + { + WriteFloat(x.X); + WriteFloat(x.Y); + WriteFloat(x.Z); + } + + public Vector3 ReadVector3() + { + float x = ReadFloat(); + float y = ReadFloat(); + float z = ReadFloat(); + return new Vector3(x, y, z); + } + + public void WriteVector4(Vector4 x) + { + WriteFloat(x.X); + WriteFloat(x.Y); + WriteFloat(x.Z); + WriteFloat(x.W); + } + + public Vector4 ReadVector4() + { + float x = ReadFloat(); + float y = ReadFloat(); + float z = ReadFloat(); + float w = ReadFloat(); + return new Vector4(x, y, z, w); + } + + + public void WriteQuaternion(Quaternion x) + { + WriteFloat(x.X); + WriteFloat(x.Y); + WriteFloat(x.Z); + WriteFloat(x.W); + } + + public Quaternion ReadQuaternion() + { + float x = ReadFloat(); + float y = ReadFloat(); + float z = ReadFloat(); + float w = ReadFloat(); + return new Quaternion(x, y, z, w); + } + + + public void WriteMatrix4x4(Matrix4x4 x) + { + WriteFloat(x.M11); + WriteFloat(x.M12); + WriteFloat(x.M13); + WriteFloat(x.M14); + WriteFloat(x.M21); + WriteFloat(x.M22); + WriteFloat(x.M23); + WriteFloat(x.M24); + WriteFloat(x.M31); + WriteFloat(x.M32); + WriteFloat(x.M33); + WriteFloat(x.M34); + WriteFloat(x.M41); + WriteFloat(x.M42); + WriteFloat(x.M43); + WriteFloat(x.M44); + } + + public Matrix4x4 ReadMatrix4x4() + { + float m11 = ReadFloat(); + float m12 = ReadFloat(); + float m13 = ReadFloat(); + float m14 = ReadFloat(); + float m21 = ReadFloat(); + float m22 = ReadFloat(); + float m23 = ReadFloat(); + float m24 = ReadFloat(); + float m31 = ReadFloat(); + float m32 = ReadFloat(); + float m33 = ReadFloat(); + float m34 = ReadFloat(); + float m41 = ReadFloat(); + float m42 = ReadFloat(); + float m43 = ReadFloat(); + float m44 = ReadFloat(); + return new Matrix4x4(m11, m12, m13, m14, + m21, m22, m23, m24, + m31, m32, m33, m34, + m41, m42, m43, m44); + } + + public void WriteObjectWithFieldTag(T obj) + { + switch (obj) + { + case int i: { WriteByte(FieldTag.INT); WriteInt(i); break; } + case long l: { WriteByte(FieldTag.LONG); WriteLong(l); break; } + case string s: { WriteByte(FieldTag.STRING); WriteString(s); break; } + case bool b: { WriteByte(FieldTag.BOOL); WriteBool(b); break; } + case short s2: { WriteByte(FieldTag.SHORT); WriteShort(s2); break; } + case float f: { WriteByte(FieldTag.FLOAT); WriteFloat(f); break; } + case double d: { WriteByte(FieldTag.DOUBLE); WriteDouble(d); break; } + case byte b2: { WriteByte(FieldTag.BYTE); WriteByte(b2); break; } + case byte[] b3: { WriteByte(FieldTag.BYTES); WriteBytes(b3); break; } + case Vector2 v2: { WriteByte(FieldTag.VECTOR2); WriteVector2(v2); break; } + case Vector3 v3: { WriteByte(FieldTag.VECTOR3); WriteVector3(v3); break; } + case Vector4 v4: { WriteByte(FieldTag.VECTOR4); WriteVector4(v4); break; } + default: throw new SerializationException("unknown object:" + obj); + } + } + + public object ReadObjectWithFieldTag() + { + return (ReadByte()) switch + { + FieldTag.INT => ReadInt(), + FieldTag.LONG => ReadLong(), + FieldTag.STRING => ReadString(), + FieldTag.BOOL => ReadBool(), + FieldTag.SHORT => ReadShort(), + FieldTag.FLOAT => ReadFloat(), + FieldTag.DOUBLE => ReadDouble(), + FieldTag.BYTE => ReadByte(), + FieldTag.BYTES => ReadBytes(), + FieldTag.VECTOR2 => ReadVector2(), + FieldTag.VECTOR3 => ReadVector3(), + FieldTag.VECTOR4 => ReadVector4(), + _ => throw new SerializationException("unknown support field tag"), + }; + } + + internal void SkipBytes() + { + int n = ReadSize(); + EnsureRead(n); + ReaderIndex += n; + } + + + public void WriteByteBufWithSize(ByteBuf o) + { + int n = o.Size; + if (n > 0) + { + WriteSize(n); + WriteBytesWithoutSize(o.Bytes, o.ReaderIndex, n); + } + else + { + WriteByte(0); + } + } + + public void WriteByteBufWithoutSize(ByteBuf o) + { + int n = o.Size; + if (n > 0) + { + WriteBytesWithoutSize(o.Bytes, o.ReaderIndex, n); + } + } + + // 有Segment相关函数后,此函数暂时废弃 + //public ByteBuf ReadInplaceByteBufWithSize() + //{ + // int n = ReadSize(); + // EnsureRead(n); + // int curReadIndex = ReaderIndex; + // ReaderIndex += n; + // return new ByteBuf(Bytes, curReadIndex, ReaderIndex); + //} + + + public bool TryReadByte(out byte x) + { + if (CanRead(1)) + { + x = Bytes[ReaderIndex++]; + return true; + } + else + { + x = 0; + return false; + } + } + + //public EDeserializeError TryDeserialize(Func action) + //{ + // var oldReadIndex = ReaderIndex; + // EDeserializeError ret = EDeserializeError.UNMARSHAL_ERR; + // try + // { + // ret = action(this); + // return ret; + // } + // finally + // { + // if (ret != EDeserializeError.OK) + // { + // ReaderIndex = oldReadIndex; + // } + // } + //} + + public EDeserializeError TryDeserializeInplaceByteBuf(int maxSize, ByteBuf inplaceTempBody) + { + //if (!CanRead(1)) { return EDeserializeError.NOT_ENOUGH; } + int oldReadIndex = ReaderIndex; + bool commit = false; + try + { + int n; + int h = Bytes[ReaderIndex]; + if (h < 0x80) + { + ReaderIndex++; + n = h; + } + else if (h < 0xc0) + { + if (!CanRead(2)) { return EDeserializeError.NOT_ENOUGH; } + n = ((h & 0x3f) << 8) | Bytes[ReaderIndex + 1]; + ReaderIndex += 2; + } + else if (h < 0xe0) + { + if (!CanRead(3)) { return EDeserializeError.NOT_ENOUGH; } + n = ((h & 0x1f) << 16) | (Bytes[ReaderIndex + 1] << 8) | Bytes[ReaderIndex + 2]; + ReaderIndex += 3; + } + else if (h < 0xf0) + { + if (!CanRead(4)) { return EDeserializeError.NOT_ENOUGH; } + n = ((h & 0x0f) << 24) | (Bytes[ReaderIndex + 1] << 16) | (Bytes[ReaderIndex + 2] << 8) | Bytes[ReaderIndex + 3]; + ReaderIndex += 4; + } + else + { + return EDeserializeError.EXCEED_SIZE; + } + + if (n > maxSize) + { + return EDeserializeError.EXCEED_SIZE; + } + if (Remaining < n) + { + return EDeserializeError.NOT_ENOUGH; + } + + int inplaceReadIndex = ReaderIndex; + ReaderIndex += n; + + inplaceTempBody.Replace(Bytes, inplaceReadIndex, ReaderIndex); + commit = true; + } + finally + { + if (!commit) + { + ReaderIndex = oldReadIndex; + } + } + + return EDeserializeError.OK; + } + + + // TODO + // 优化 未知大小的对象,比如 bean 的序列化 + public void SkipUnknownField(int tagId) + { + int tagType = tagId & FieldTag.TAG_MASK; + switch (tagType) + { + case FieldTag.BOOL: + ReadBool(); + break; + case FieldTag.BYTE: + ReadByte(); + break; + case FieldTag.SHORT: + ReadShort(); + break; + case FieldTag.FSHORT: + ReadFshort(); + break; + case FieldTag.INT: + ReadInt(); + break; + case FieldTag.FINT: + ReadFint(); + break; + case FieldTag.LONG: + ReadLong(); + break; + case FieldTag.FLONG: + ReadFlong(); + break; + case FieldTag.FLOAT: + ReadFloat(); + break; + case FieldTag.DOUBLE: + ReadDouble(); + break; + case FieldTag.VECTOR2: + ReadVector2(); + break; + case FieldTag.VECTOR3: + ReadVector3(); + break; + case FieldTag.VECTOR4: + ReadVector4(); + break; + case FieldTag.STRING: + case FieldTag.BYTES: + case FieldTag.ARRAY: + case FieldTag.LIST: + case FieldTag.SET: + case FieldTag.MAP: + case FieldTag.BEAN: + { + SkipBytes(); + break; + } + case FieldTag.DYNAMIC_BEAN: + { + int type = ReadInt(); + if (type != 0) + { + SkipBytes(); + } + break; + } + default: + throw new SerializationException(); + } + } + + public void WriteRawTag(byte b1) + { + EnsureWrite(1); + Bytes[WriterIndex++] = b1; + } + + public void WriteRawTag(byte b1, byte b2) + { + EnsureWrite(2); + Bytes[WriterIndex] = b1; + Bytes[WriterIndex + 1] = b2; + WriterIndex += 2; + } + + public void WriteRawTag(byte b1, byte b2, byte b3) + { + EnsureWrite(3); + Bytes[WriterIndex] = b1; + Bytes[WriterIndex + 1] = b2; + Bytes[WriterIndex + 2] = b3; + WriterIndex += 3; + } + + #region segment + + + public void BeginWriteSegment(out int oldSize) + { + oldSize = Size; + EnsureWrite(1); + WriterIndex += 1; + } + + public void EndWriteSegment(int oldSize) + { + int startPos = ReaderIndex + oldSize; + int segmentSize = WriterIndex - startPos - 1; + + // 0 111 1111 + if (segmentSize < 0x80) + { + Bytes[startPos] = (byte)segmentSize; + } + else if (segmentSize < 0x4000) // 10 11 1111, - + { + EnsureWrite(1); + Bytes[WriterIndex] = Bytes[startPos + 1]; + Bytes[startPos + 1] = (byte)segmentSize; + + Bytes[startPos] = (byte)((segmentSize >> 8) | 0x80); + WriterIndex += 1; + } + else if (segmentSize < 0x200000) // 110 1 1111, -,- + { + EnsureWrite(2); + Bytes[WriterIndex + 1] = Bytes[startPos + 2]; + Bytes[startPos + 2] = (byte)segmentSize; + + Bytes[WriterIndex] = Bytes[startPos + 1]; + Bytes[startPos + 1] = (byte)(segmentSize >> 8); + + Bytes[startPos] = (byte)((segmentSize >> 16) | 0xc0); + WriterIndex += 2; + } + else if (segmentSize < 0x10000000) // 1110 1111,-,-,- + { + EnsureWrite(3); + Bytes[WriterIndex + 2] = Bytes[startPos + 3]; + Bytes[startPos + 3] = (byte)segmentSize; + + Bytes[WriterIndex + 1] = Bytes[startPos + 2]; + Bytes[startPos + 2] = (byte)(segmentSize >> 8); + + Bytes[WriterIndex] = Bytes[startPos + 1]; + Bytes[startPos + 1] = (byte)(segmentSize >> 16); + + Bytes[startPos] = (byte)((segmentSize >> 24) | 0xe0); + WriterIndex += 3; + } + else + { + throw new SerializationException("exceed max segment size"); + } + } + + public void ReadSegment(out int startIndex, out int segmentSize) + { + EnsureRead(1); + int h = Bytes[ReaderIndex++]; + + startIndex = ReaderIndex; + + if (h < 0x80) + { + segmentSize = h; + ReaderIndex += segmentSize; + } + else if (h < 0xc0) + { + EnsureRead(1); + segmentSize = ((h & 0x3f) << 8) | Bytes[ReaderIndex]; + int endPos = ReaderIndex + segmentSize; + Bytes[ReaderIndex] = Bytes[endPos]; + ReaderIndex += segmentSize + 1; + } + else if (h < 0xe0) + { + EnsureRead(2); + segmentSize = ((h & 0x1f) << 16) | ((int)Bytes[ReaderIndex] << 8) | Bytes[ReaderIndex + 1]; + int endPos = ReaderIndex + segmentSize; + Bytes[ReaderIndex] = Bytes[endPos]; + Bytes[ReaderIndex + 1] = Bytes[endPos + 1]; + ReaderIndex += segmentSize + 2; + } + else if (h < 0xf0) + { + EnsureRead(3); + segmentSize = ((h & 0x0f) << 24) | ((int)Bytes[ReaderIndex] << 16) | ((int)Bytes[ReaderIndex + 1] << 8) | Bytes[ReaderIndex + 2]; + int endPos = ReaderIndex + segmentSize; + Bytes[ReaderIndex] = Bytes[endPos]; + Bytes[ReaderIndex + 1] = Bytes[endPos + 1]; + Bytes[ReaderIndex + 2] = Bytes[endPos + 2]; + ReaderIndex += segmentSize + 3; + } + else + { + throw new SerializationException("exceed max size"); + } + if (ReaderIndex > WriterIndex) + { + throw new SerializationException("segment data not enough"); + } + } + + public void ReadSegment(ByteBuf buf) + { + ReadSegment(out int startPos, out var size); + buf.Bytes = Bytes; + buf.ReaderIndex = startPos; + buf.WriterIndex = startPos + size; + } + + public void EnterSegment(out SegmentSaveState saveState) + { + ReadSegment(out int startPos, out int size); + + saveState = new SegmentSaveState(ReaderIndex, WriterIndex); + ReaderIndex = startPos; + WriterIndex = startPos + size; + } + + public void LeaveSegment(SegmentSaveState saveState) + { + ReaderIndex = saveState.ReaderIndex; + WriterIndex = saveState.WriterIndex; + } + + #endregion + + public override string ToString() + { + string[] datas = new string[WriterIndex - ReaderIndex]; + for (var i = ReaderIndex; i < WriterIndex; i++) + { + datas[i - ReaderIndex] = Bytes[i].ToString("X2"); + } + return string.Join(".", datas); + } + + public override bool Equals(object obj) + { + return (obj is ByteBuf other) && Equals(other); + } + + public bool Equals(ByteBuf other) + { + if (other == null) + { + return false; + } + if (Size != other.Size) + { + return false; + } + for (int i = 0, n = Size; i < n; i++) + { + if (Bytes[ReaderIndex + i] != other.Bytes[other.ReaderIndex + i]) + { + return false; + } + } + return true; + } + + public object Clone() + { + return new ByteBuf(CopyData()); + } + + + public static ByteBuf FromString(string value) + { + var ss = value.Split(','); + byte[] data = new byte[ss.Length]; + for (int i = 0; i < data.Length; i++) + { + data[i] = byte.Parse(ss[i]); + } + return new ByteBuf(data); + } + + public override int GetHashCode() + { + int hash = 17; + for (int i = ReaderIndex; i < WriterIndex; i++) + { + hash = hash * 23 + Bytes[i]; + } + return hash; + } + + public void Release() + { + _releaser?.Invoke(this); + } + } +} diff --git a/src/LubanAssistant/Source/Serialization/EUnmarshalError.cs b/src/LubanAssistant/Source/Serialization/EUnmarshalError.cs new file mode 100644 index 0000000..1293c51 --- /dev/null +++ b/src/LubanAssistant/Source/Serialization/EUnmarshalError.cs @@ -0,0 +1,10 @@ +namespace Bright.Serialization +{ + public enum EDeserializeError + { + OK, + NOT_ENOUGH, + EXCEED_SIZE, + // UNMARSHAL_ERR, + } +} diff --git a/src/LubanAssistant/Source/Serialization/FieldTag.cs b/src/LubanAssistant/Source/Serialization/FieldTag.cs new file mode 100644 index 0000000..ff67c6c --- /dev/null +++ b/src/LubanAssistant/Source/Serialization/FieldTag.cs @@ -0,0 +1,42 @@ +namespace Bright.Serialization +{ + + // 把 int,long,string,bool 调整到最小 + // 这样 marshal compatible write(field_id << tag_shift | tag_id) < 2^7 能在一个字节 + // 内序列化, 优化序列化最终大小 +#pragma warning disable CA1720 // 标识符包含类型名称 + public static class FieldTag + { + public const int + INT = 0, + LONG = 1, + STRING = 2, + BOOL = 3, + + BYTE = 4, + SHORT = 5, + FSHORT = 6, + FINT = 7, + FLONG = 8, + FLOAT = 9, + DOUBLE = 10, + BYTES = 11, + ARRAY = 12, + LIST = 13, + SET = 14, + MAP = 15, + BEAN = 16, + TEXT = 17, + VECTOR2 = 18, + VECTOR3 = 19, + VECTOR4 = 20, + DYNAMIC_BEAN = 21, + + NOT_USE = 22; + + + public const int TAG_SHIFT = 5; + public const int TAG_MASK = (1 << TAG_SHIFT) - 1; + } +#pragma warning restore CA1720 // 标识符包含类型名称 +} diff --git a/src/LubanAssistant/Source/Serialization/ISerializable.cs b/src/LubanAssistant/Source/Serialization/ISerializable.cs new file mode 100644 index 0000000..b0eff60 --- /dev/null +++ b/src/LubanAssistant/Source/Serialization/ISerializable.cs @@ -0,0 +1,13 @@ +namespace Bright.Serialization +{ + /// + /// 非兼容binary序列化 + /// + public interface ISerializable + { + void Serialize(ByteBuf os); + + void Deserialize(ByteBuf os); + } + +} diff --git a/src/LubanAssistant/Source/Serialization/ITypeId.cs b/src/LubanAssistant/Source/Serialization/ITypeId.cs new file mode 100644 index 0000000..2b8d71a --- /dev/null +++ b/src/LubanAssistant/Source/Serialization/ITypeId.cs @@ -0,0 +1,7 @@ +namespace Bright.Serialization +{ + public interface ITypeId + { + int GetTypeId(); + } +} diff --git a/src/LubanAssistant/Source/Serialization/SerializationException.cs b/src/LubanAssistant/Source/Serialization/SerializationException.cs new file mode 100644 index 0000000..292f00e --- /dev/null +++ b/src/LubanAssistant/Source/Serialization/SerializationException.cs @@ -0,0 +1,24 @@ +#define CPU_SUPPORT_MEMORY_NOT_ALIGN //CPU 是否支持读取非对齐内存 + +using System; + + +/// +/// TODO +/// 1. 整理代码 +/// 2. 优化序列化 (像这样 data[endPos + 1] = (byte)(x >> 8) 挨个字节赋值总感觉很低效,能优化吗) +/// + + +namespace Bright.Serialization +{ + public class SerializationException : Exception + { + public SerializationException() { } + public SerializationException(string msg) : base(msg) { } + + public SerializationException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/src/LubanAssistant/Source/Utils/DataLoaderUtil.cs b/src/LubanAssistant/Source/Utils/DataLoaderUtil.cs new file mode 100644 index 0000000..a61dfea --- /dev/null +++ b/src/LubanAssistant/Source/Utils/DataLoaderUtil.cs @@ -0,0 +1,223 @@ +using Bright.Time; +using Luban.Common.Utils; +using Luban.Job.Cfg.DataCreators; +using Luban.Job.Cfg.Datas; +using Luban.Job.Cfg.DataSources; +using Luban.Job.Cfg.Defs; +using Luban.Job.Common.Types; +using Luban.Job.Common.Utils; +using Luban.Server.Common; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace Luban.Job.Cfg.Utils +{ + public static class DataLoaderUtil + { + private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger(); + + public class InputFileInfo + { + public string MD5 { get; set; } + + public string OriginFile { get; set; } + + public string ActualFile { get; set; } + + public string SheetName { get; set; } + } + + public static async Task> CollectInputFilesAsync(IAgent agent, IEnumerable files, string dataDir) + { + var collectTasks = new List>>(); + foreach (var file in files) + { + (var actualFile, var sheetName) = FileUtil.SplitFileAndSheetName(FileUtil.Standardize(file)); + var actualFullPath = FileUtil.Combine(dataDir, actualFile); + var originFullPath = FileUtil.Combine(dataDir, file); + //s_logger.Info("== get input file:{file} actualFile:{actual}", file, actualFile); + + collectTasks.Add(Task.Run(async () => + { + var fileOrDirContent = await agent.GetFileOrDirectoryAsync(actualFullPath, DataSourceFactory.validDataSourceSuffixes); + if (fileOrDirContent.IsFile) + { + return new List { new InputFileInfo() { OriginFile = file, ActualFile = actualFullPath, SheetName = sheetName, MD5 = fileOrDirContent.Md5 } }; + } + else + { + return fileOrDirContent.SubFiles.Select(f => new InputFileInfo() { OriginFile = f.FilePath, ActualFile = f.FilePath, MD5 = f.MD5 }).ToList(); + } + })); + } + + var allFiles = new List(); + foreach (var t in collectTasks) + { + allFiles.AddRange(await t); + } + return allFiles; + } + + //private async Task> CollectInputFilesAsync(RemoteAgent agent, DefTable table, string dataDir) + //{ + // var collectTasks = new List>>(); + // foreach (var file in table.InputFiles) + // return CollectInputFilesAsync(agent, table.InputFiles, dataDir) + //} + + public static async Task GenerateLoadRecordFromFileTasksAsync(IAgent agent, DefTable table, string dataDir, List inputFiles2, List>> tasks) + { + var inputFileInfos = await CollectInputFilesAsync(agent, inputFiles2, dataDir); + + // check cache (table, exporttestdata) -> (list, List) + // (md5, sheetName,exportTestData) -> (value_type, List) + + foreach (var file in inputFileInfos) + { + var actualFile = file.ActualFile; + //s_logger.Info("== get input file:{file} actualFile:{actual}", file, actualFile); + + tasks.Add(Task.Run(async () => + { + var res = LoadCfgRecords(table.ValueTType, + file.OriginFile, + file.SheetName, + await agent.GetFromCacheOrReadAllBytesAsync(file.ActualFile, file.MD5), + FileUtil.IsExcelFile(file.ActualFile)); + + return res; + })); + } + } + + public static async Task LoadTableAsync(IAgent agent, DefTable table, string dataDir, string patchName, string patchDataDir) + { + var mainLoadTasks = new List>>(); + var mainGenerateTask = GenerateLoadRecordFromFileTasksAsync(agent, table, dataDir, table.InputFiles, mainLoadTasks); + + var patchLoadTasks = new List>>(); + + Task patchGenerateTask = null; + if (!string.IsNullOrWhiteSpace(patchName)) + { + var patchInputFiles = table.GetPatchInputFiles(patchName); + if (patchInputFiles != null) + { + patchGenerateTask = GenerateLoadRecordFromFileTasksAsync(agent, table, patchDataDir, patchInputFiles, patchLoadTasks); + } + } + + await mainGenerateTask; + + var mainRecords = new List(256); + foreach (var task in mainLoadTasks) + { + mainRecords.AddRange(await task); + } + s_logger.Trace("== load main records. count:{count}", mainRecords.Count); + + List patchRecords = null; + if (patchGenerateTask != null) + { + patchRecords = new List(64); + await patchGenerateTask; + foreach (var task in patchLoadTasks) + { + patchRecords.AddRange(await task); + } + s_logger.Trace("== load patch records. count:{count}", patchRecords.Count); + } + + table.Assembly.AddDataTable(table, mainRecords, patchRecords); + + s_logger.Trace("table:{name} record num:{num}", table.FullName, mainRecords.Count); + } + + public static async Task LoadCfgDataAsync(IAgent agent, DefAssembly ass, string dataDir, string patchName, string patchDataDir) + { + var ctx = agent; + List exportTables = ass.Types.Values.Where(t => t is DefTable ct && ct.NeedExport).Select(t => (DefTable)t).ToList(); + var genDataTasks = new List(); + var outputDataFiles = new ConcurrentBag(); + long genDataStartTime = TimeUtil.NowMillis; + + foreach (DefTable c in exportTables) + { + var table = c; + genDataTasks.Add(Task.Run(async () => + { + long beginTime = TimeUtil.NowMillis; + await LoadTableAsync(agent, table, dataDir, patchName, patchDataDir); + long endTime = TimeUtil.NowMillis; + if (endTime - beginTime > 100) + { + ctx.Info("====== load {0} cost {1} ms ======", table.FullName, (endTime - beginTime)); + } + })); + } + await Task.WhenAll(genDataTasks.ToArray()); + } + + public static List LoadCfgRecords(TBean recordType, string originFile, string sheetName, byte[] content, bool multiRecord) + { + // (md5,sheet,multiRecord,exportTestData) -> (valuetype, List<(datas)>) + var dataSource = DataSourceFactory.Create(originFile, sheetName, new MemoryStream(content)); + try + { + if (multiRecord) + { + return dataSource.ReadMulti(recordType); + } + else + { + Record record = dataSource.ReadOne(recordType); + return record != null ? new List { record } : new List(); + } + } + catch (DataCreateException dce) + { + if (string.IsNullOrWhiteSpace(dce.OriginDataLocation)) + { + dce.OriginDataLocation = originFile; + } + throw; + } + catch (Exception e) + { + throw new Exception($"配置文件:{originFile} 生成失败.", e); + } + } + +#if !LUBAN_ASSISTANT + public static async Task LoadTextTablesAsync(IAgent agent, DefAssembly ass, string baseDir, string textTableFiles) + { + var tasks = new List>(); + var files = textTableFiles.Split(','); + foreach (var file in await CollectInputFilesAsync(agent, files, baseDir)) + { + tasks.Add(agent.GetFromCacheOrReadAllBytesAsync(file.ActualFile, file.MD5)); + } + + var textTable = ass.ExportTextTable; + for (int i = 0; i < tasks.Count; i++) + { + var bytes = await tasks[i]; + try + { + textTable.LoadFromFile(files[i], bytes); + } + catch (Exception e) + { + throw new Exception($"load text table file:{files[i]} fail", e); + } + } + } +#endif + + } +} diff --git a/src/LubanAssistant/ThisAddIn.cs b/src/LubanAssistant/ThisAddIn.cs index 9aa8a08..c688b8a 100644 --- a/src/LubanAssistant/ThisAddIn.cs +++ b/src/LubanAssistant/ThisAddIn.cs @@ -6,6 +6,7 @@ using System.Xml.Linq; using Excel = Microsoft.Office.Interop.Excel; using Office = Microsoft.Office.Core; using Microsoft.Office.Tools.Excel; +using Microsoft.Office.Tools.Ribbon; namespace LubanAssistant { @@ -19,6 +20,17 @@ namespace LubanAssistant { } + //protected override Microsoft.Office.Core.IRibbonExtensibility CreateRibbonExtensibilityObject() + //{ + // return new ToolTab(); + //} + + + protected override IRibbonExtension[] CreateRibbonObjects() + { + return new IRibbonExtension[] { new AssistantTab() }; + } + #region VSTO 生成的代码 /// @@ -30,7 +42,7 @@ namespace LubanAssistant this.Startup += new System.EventHandler(ThisAddIn_Startup); this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown); } - + #endregion } } diff --git a/src/LubanAssistant/app.config b/src/LubanAssistant/app.config new file mode 100644 index 0000000..fc8bbbf --- /dev/null +++ b/src/LubanAssistant/app.config @@ -0,0 +1,27 @@ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/LubanAssistant/packages.config b/src/LubanAssistant/packages.config new file mode 100644 index 0000000..27dbb33 --- /dev/null +++ b/src/LubanAssistant/packages.config @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file