From 9e4ba72950fe592241f28ee936acae89dc620389 Mon Sep 17 00:00:00 2001 From: walon Date: Thu, 17 Jun 2021 17:20:19 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E6=94=AF=E6=8C=81=20=E4=B8=BB=E7=89=88?= =?UTF-8?q?=E6=9C=AC+=E5=88=86=E6=94=AF=E7=89=88=E6=9C=AC=E7=9A=84=20?= =?UTF-8?q?=E5=A4=9A=E5=88=86=E6=94=AF=E6=95=B0=E6=8D=AE=E5=90=88=E5=B9=B6?= =?UTF-8?q?=EF=BC=8C=E7=94=A8=E4=BA=8E=E5=8F=91=E5=B8=83=E5=A4=9A=E5=9C=B0?= =?UTF-8?q?=E5=8C=BA=E7=89=88=E6=9C=AC=E6=9E=81=E5=85=B6=E6=9C=89=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- config/Datas/l10n/cn/test/full_type_cn1.xlsx | Bin 0 -> 12356 bytes config/Datas/l10n/cn/test/full_type_cn2.xlsx | Bin 0 -> 12404 bytes config/Datas/l10n/en/texx/full_type_en.xlsx | Bin 0 -> 12409 bytes config/Defines/__root__.xml | 4 + config/Defines/test.xml | 2 +- config/branch生成.bat | 14 +++ src/Luban.Job.Cfg/Source/Datas/Record.cs | 2 + src/Luban.Job.Cfg/Source/Defs/CfgDefLoader.cs | 42 ++++++- src/Luban.Job.Cfg/Source/Defs/DefAssembly.cs | 62 ++++++++-- src/Luban.Job.Cfg/Source/Defs/DefBean.cs | 2 +- src/Luban.Job.Cfg/Source/Defs/DefField.cs | 2 +- src/Luban.Job.Cfg/Source/Defs/DefTable.cs | 22 +++- src/Luban.Job.Cfg/Source/JobController.cs | 38 ++++-- src/Luban.Job.Cfg/Source/RawDefs/Branch.cs | 18 +++ src/Luban.Job.Cfg/Source/RawDefs/CfgBean.cs | 2 +- src/Luban.Job.Cfg/Source/RawDefs/CfgField.cs | 2 +- src/Luban.Job.Cfg/Source/RawDefs/Defines.cs | 5 +- src/Luban.Job.Cfg/Source/RawDefs/Group.cs | 2 +- src/Luban.Job.Cfg/Source/RawDefs/Service.cs | 2 +- src/Luban.Job.Cfg/Source/RawDefs/Table.cs | 4 +- .../TypeVisitors/DeepCompareTypeDefine.cs | 2 +- .../Source/Utils/DataExporterUtil.cs | 1 - .../Source/Utils/DataLoaderUtil.cs | 67 +++++++--- src/Luban.Job.Cfg/Source/ValidatorContext.cs | 115 +++++++++++++++--- .../Source/Validators/RefValidator.cs | 2 +- .../Source/Validators/ValidatorFactory.cs | 2 +- src/Luban.Job.Cfg/Source/l10n/TextTable.cs | 4 +- 28 files changed, 335 insertions(+), 85 deletions(-) create mode 100644 config/Datas/l10n/cn/test/full_type_cn1.xlsx create mode 100644 config/Datas/l10n/cn/test/full_type_cn2.xlsx create mode 100644 config/Datas/l10n/en/texx/full_type_en.xlsx create mode 100644 config/branch生成.bat create mode 100644 src/Luban.Job.Cfg/Source/RawDefs/Branch.cs diff --git a/README.md b/README.md index 39278fa..f1341a4 100644 --- a/README.md +++ b/README.md @@ -53,8 +53,8 @@ Luban适合有以下需求的开发者: - 支持res资源标记。可以一键导出配置中引用的所有资源列表(icon,ui,assetbundle等等) - 生成代码良好模块化。 - **支持文本静态本地化。导出时所有text类型数据正确替换为最终的本地化字符串。** +- **支持main + braches 多地区版本数据。对于需要针对不同地区有部分差异配置的海外项目非常有用。 - **[TODO] 支持文本动态本地化。运行时动态切换所有text类型数据为目标本地化字符串。** -- **[TODO] 支持main + braches 多地区版本数据。对于需要针对不同地区有部分差异配置的海外项目非常有用。** - 支持主流的游戏开发语言 - c++ (11+) - c# (.net framework 4+. dotnet core 3+) diff --git a/config/Datas/l10n/cn/test/full_type_cn1.xlsx b/config/Datas/l10n/cn/test/full_type_cn1.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..8d3025fbd68d070ab81edd26fafc76b796f99912 GIT binary patch literal 12356 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 literal 0 HcmV?d00001 diff --git a/config/Datas/l10n/cn/test/full_type_cn2.xlsx b/config/Datas/l10n/cn/test/full_type_cn2.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..f52591064b64ba923a2a6b81add68b5a77d4962c GIT binary patch literal 12404 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 literal 0 HcmV?d00001 diff --git a/config/Datas/l10n/en/texx/full_type_en.xlsx b/config/Datas/l10n/en/texx/full_type_en.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..bb5374a38d95ddc1ae8946c988d67cab5badf216 GIT binary patch literal 12409 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 editor分组 + + + + diff --git a/config/Defines/test.xml b/config/Defines/test.xml index 2d32cbe..44a65bc 100644 --- a/config/Defines/test.xml +++ b/config/Defines/test.xml @@ -79,7 +79,7 @@ - 最常见的普通 key-value表 +
最常见的普通 key-value表 diff --git a/config/branch生成.bat b/config/branch生成.bat new file mode 100644 index 0000000..d79687a --- /dev/null +++ b/config/branch生成.bat @@ -0,0 +1,14 @@ +..\src\Luban.Client\bin\Debug\net5.0\Luban.Client.exe ^ + -h %LUBAN_SERVER_IP% ^ + -j cfg ^ + -- ^ + -d Defines/__root__.xml ^ + --input_data_dir Datas ^ + --output_data_dir output_lua ^ + -s client ^ + --gen_types data_lua ^ + --export_test_data ^ + --branch cn ^ + --branch_input_data_dir Datas/l10n/cn + +pause \ No newline at end of file diff --git a/src/Luban.Job.Cfg/Source/Datas/Record.cs b/src/Luban.Job.Cfg/Source/Datas/Record.cs index 67d0450..cfe442a 100644 --- a/src/Luban.Job.Cfg/Source/Datas/Record.cs +++ b/src/Luban.Job.Cfg/Source/Datas/Record.cs @@ -12,6 +12,8 @@ namespace Luban.Job.Cfg.Datas public string Source { get; } + public int Index { get; set; } + public Record(DBean data, string source) { Data = data; diff --git a/src/Luban.Job.Cfg/Source/Defs/CfgDefLoader.cs b/src/Luban.Job.Cfg/Source/Defs/CfgDefLoader.cs index f8f2f61..b15695d 100644 --- a/src/Luban.Job.Cfg/Source/Defs/CfgDefLoader.cs +++ b/src/Luban.Job.Cfg/Source/Defs/CfgDefLoader.cs @@ -1,5 +1,5 @@ using Luban.Common.Utils; -using Luban.Config.Common.RawDefs; +using Luban.Job.Cfg.RawDefs; using Luban.Job.Common.Defs; using Luban.Job.Common.RawDefs; using Luban.Server.Common; @@ -14,6 +14,8 @@ namespace Luban.Job.Cfg.Defs { private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger(); + private readonly List _branches = new(); + private readonly List
_cfgTables = new List
(); private readonly List _cfgServices = new List(); @@ -24,11 +26,13 @@ namespace Luban.Job.Cfg.Defs public CfgDefLoader(RemoteAgent agent) : base(agent) { + RegisterRootDefineHandler("branch", AddBranch); RegisterRootDefineHandler("service", AddService); RegisterRootDefineHandler("group", AddGroup); RegisterModuleDefineHandler("table", AddTable); + IsBeanFieldMustDefineId = false; } @@ -37,6 +41,7 @@ namespace Luban.Job.Cfg.Defs return new Defines() { TopModule = TopModule, + Branches = _branches, Consts = this._consts, Enums = _enums, Beans = _beans, @@ -47,6 +52,21 @@ namespace Luban.Job.Cfg.Defs } + private static readonly List _branchRequireAttrs = new List { "name" }; + private void AddBranch(XElement e) + { + var branchName = e.Attribute("name").Value; + if (string.IsNullOrWhiteSpace(branchName)) + { + throw new Exception("branch 属性name不能为空"); + } + if (this._branches.Any(b => b.Name == branchName)) + { + throw new Exception($"branch {branchName} 重复"); + } + _branches.Add(new Branch(branchName)); + } + private static readonly List _groupOptionalAttrs = new List { "default" }; private static readonly List _groupRequireAttrs = new List { "name" }; @@ -132,7 +152,7 @@ namespace Luban.Job.Cfg.Defs _cfgServices.Add(new Service() { Name = name, Manager = manager, Groups = groups, Refs = refs }); } - private readonly List _tableOptionalAttrs = new List { "index", "mode", "group" }; + private readonly List _tableOptionalAttrs = new List { "index", "mode", "group", "branch_input" }; private readonly List _tableRequireAttrs = new List { "name", "value", "input" }; @@ -236,6 +256,24 @@ namespace Luban.Job.Cfg.Defs } p.InputFiles.AddRange(XmlUtil.GetRequiredAttribute(e, "input").Split(',')); + var branchInputAttr = e.Attribute("branch_input"); + if (branchInputAttr != null) + { + foreach (var subBranchStr in branchInputAttr.Value.Split('|').Select(s => s.Trim()).Where(s => !string.IsNullOrWhiteSpace(s))) + { + var nameAndDirs = subBranchStr.Split(':'); + if (nameAndDirs.Length != 2) + { + throw new Exception($"定义文件:{CurImportFile} table:{p.Name} branch_input:{subBranchStr} 定义不合法"); + } + var branchDirs = nameAndDirs[1].Split(',', ';').ToList(); + if (!p.BranchInputFiles.TryAdd(nameAndDirs[0], branchDirs)) + { + throw new Exception($"定义文件:{CurImportFile} table:{p.Name} branch_input:{subBranchStr} 子branch:{nameAndDirs[0]} 重复"); + } + } + } + if (!_name2CfgTable.TryAdd(p.Name, p)) { var exist = _name2CfgTable[p.Name]; diff --git a/src/Luban.Job.Cfg/Source/Defs/DefAssembly.cs b/src/Luban.Job.Cfg/Source/Defs/DefAssembly.cs index 33d3330..d2d4888 100644 --- a/src/Luban.Job.Cfg/Source/Defs/DefAssembly.cs +++ b/src/Luban.Job.Cfg/Source/Defs/DefAssembly.cs @@ -1,6 +1,6 @@ -using Luban.Config.Common.RawDefs; using Luban.Job.Cfg.Datas; using Luban.Job.Cfg.l10n; +using Luban.Job.Cfg.RawDefs; using Luban.Job.Cfg.TypeVisitors; using Luban.Job.Common.Defs; using Luban.Server.Common; @@ -11,12 +11,31 @@ using System.Linq; namespace Luban.Job.Cfg.Defs { + public class TableDataInfo + { + public List MainRecords { get; } + + public List BranchRecords { get; } + + public List FinalRecords { get; set; } + + public Dictionary FinalRecordMap { get; set; } + + public TableDataInfo(List mainRecords, List branchRecords) + { + MainRecords = mainRecords; + BranchRecords = branchRecords; + } + } + public class DefAssembly : DefAssemblyBase { private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger(); public Service CfgTargetService { get; private set; } + public Branch TargetBranch { get; private set; } + public TimeZoneInfo TimeZone { get; } public DefAssembly(TimeZoneInfo timezone) @@ -33,10 +52,11 @@ namespace Luban.Job.Cfg.Defs return groups.Any(g => CfgTargetService.Groups.Contains(g)); } + private readonly List _branches = new List(); + private readonly List _cfgServices = new List(); - private readonly ConcurrentDictionary> _recordsByTables = new(); - private readonly ConcurrentDictionary> _recordsMapByTables = new(); + private readonly ConcurrentDictionary _recordsByTables = new(); public Dictionary CfgTables { get; } = new Dictionary(); @@ -52,6 +72,11 @@ namespace Luban.Job.Cfg.Defs NotConvertTextSet = new NotConvertTextSet(); } + public Branch GetBranch(string name) + { + return _branches.Find(b => b.Name == name); + } + public void AddCfgTable(DefTable table) { if (!CfgTables.TryAdd(table.FullName, table)) @@ -65,24 +90,24 @@ namespace Luban.Job.Cfg.Defs return CfgTables.TryGetValue(name, out var t) ? t : null; } - public void AddDataTable(DefTable table, List records) + public void AddDataTable(DefTable table, List mainRecords, List branchRecords) { - _recordsByTables[table.FullName] = records; + _recordsByTables[table.FullName] = new TableDataInfo(mainRecords, branchRecords); } - public void SetDataTableMap(DefTable table, Dictionary recordMap) - { - _recordsMapByTables[table.FullName] = recordMap; - } + //public void SetDataTableMap(DefTable table, Dictionary recordMap) + //{ + // _recordsByTables[table.FullName].FinalRecordMap = recordMap; + //} public List GetTableDataList(DefTable table) { - return _recordsByTables[table.FullName]; + return _recordsByTables[table.FullName].FinalRecords; } - public Dictionary GetTableDataMap(DefTable table) + public TableDataInfo GetTableDataInfo(DefTable table) { - return _recordsMapByTables[table.FullName]; + return _recordsByTables[table.FullName]; } public List GetExportTables() @@ -122,7 +147,7 @@ namespace Luban.Job.Cfg.Defs return refTypes.Values.ToList(); } - public void Load(string outputService, Defines defines, RemoteAgent agent) + public void Load(string outputService, string branchName, Defines defines, RemoteAgent agent) { this.Agent = agent; SupportDatetimeType = true; @@ -136,6 +161,17 @@ namespace Luban.Job.Cfg.Defs throw new ArgumentException($"service:{outputService} not exists"); } + if (!string.IsNullOrWhiteSpace(branchName)) + { + TargetBranch = defines.Branches.Find(b => b.Name == branchName); + if (TargetBranch == null) + { + throw new Exception($"branch {branchName} not in valid branch set"); + } + } + + this._branches.AddRange(defines.Branches); + foreach (var c in defines.Consts) { AddType(new DefConst(c)); diff --git a/src/Luban.Job.Cfg/Source/Defs/DefBean.cs b/src/Luban.Job.Cfg/Source/Defs/DefBean.cs index b93cd1a..1167453 100644 --- a/src/Luban.Job.Cfg/Source/Defs/DefBean.cs +++ b/src/Luban.Job.Cfg/Source/Defs/DefBean.cs @@ -1,5 +1,5 @@ using Luban.Common.Utils; -using Luban.Config.Common.RawDefs; +using Luban.Job.Cfg.RawDefs; using Luban.Job.Cfg.TypeVisitors; using Luban.Job.Common.Defs; using System; diff --git a/src/Luban.Job.Cfg/Source/Defs/DefField.cs b/src/Luban.Job.Cfg/Source/Defs/DefField.cs index 7ebda3a..8a64bdd 100644 --- a/src/Luban.Job.Cfg/Source/Defs/DefField.cs +++ b/src/Luban.Job.Cfg/Source/Defs/DefField.cs @@ -1,5 +1,5 @@ using Luban.Common.Utils; -using Luban.Config.Common.RawDefs; +using Luban.Job.Cfg.RawDefs; using Luban.Job.Cfg.Validators; using Luban.Job.Common.Defs; using Luban.Job.Common.Types; diff --git a/src/Luban.Job.Cfg/Source/Defs/DefTable.cs b/src/Luban.Job.Cfg/Source/Defs/DefTable.cs index 5acd608..58ebba3 100644 --- a/src/Luban.Job.Cfg/Source/Defs/DefTable.cs +++ b/src/Luban.Job.Cfg/Source/Defs/DefTable.cs @@ -1,4 +1,4 @@ -using Luban.Config.Common.RawDefs; +using Luban.Job.Cfg.RawDefs; using Luban.Job.Common.Types; using System; using System.Collections.Generic; @@ -19,6 +19,7 @@ namespace Luban.Job.Cfg.Defs Mode = b.Mode; InputFiles = b.InputFiles; Groups = b.Groups; + _branchInputFiles = b.BranchInputFiles; } @@ -36,6 +37,8 @@ namespace Luban.Job.Cfg.Defs public List InputFiles { get; } + private readonly Dictionary> _branchInputFiles; + public List Groups { get; } public TType KeyTType { get; private set; } @@ -62,11 +65,24 @@ namespace Luban.Job.Cfg.Defs public string JsonOutputDataFile => $"{FullName}.json"; + public List GetBranchInputFiles(string branchName) + { + return _branchInputFiles.GetValueOrDefault(branchName); + } + public override void Compile() { - var pass = Assembly; + var ass = Assembly; - if ((ValueTType = (TBean)pass.CreateType(Namespace, ValueType)) == null) + foreach (var branchName in _branchInputFiles.Keys) + { + if (ass.GetBranch(branchName) == null) + { + throw new Exception($"table:{FullName} branch_input branch:{branchName} 不存在"); + } + } + + if ((ValueTType = (TBean)ass.CreateType(Namespace, ValueType)) == null) { throw new Exception($"table:{FullName} 的 value类型:{ValueType} 不存在"); } diff --git a/src/Luban.Job.Cfg/Source/JobController.cs b/src/Luban.Job.Cfg/Source/JobController.cs index 5a0cb80..0c49b61 100644 --- a/src/Luban.Job.Cfg/Source/JobController.cs +++ b/src/Luban.Job.Cfg/Source/JobController.cs @@ -58,7 +58,7 @@ namespace Luban.Job.Cfg [Option("export_test_data", Required = false, HelpText = "export test data")] public bool ExportTestData { get; set; } = false; - [Option('t', "i10n_timezone", Required = false, HelpText = "timezone")] + [Option('t', "l10n_timezone", Required = false, HelpText = "timezone")] public string TimeZone { get; set; } [Option("input_l10n_text_files", Required = false, HelpText = "input l10n text table files. can be multi, sep by ','")] @@ -66,6 +66,12 @@ namespace Luban.Job.Cfg [Option("output_l10n_not_converted_text_file", Required = false, HelpText = "the file save not converted l10n texts.")] public string OutputNotConvertTextFile { get; set; } + + [Option("branch", Required = false, HelpText = "branch name")] + public string BranchName { get; set; } + + [Option("branch_input_data_dir", Required = false, HelpText = "branch input data root dir")] + public string BranchInputDataDir { get; set; } } private ICodeRender CreateCodeRender(string genType) @@ -95,7 +101,7 @@ namespace Luban.Job.Cfg } - private static bool TryParseArg(List args, out GenArgs result, out string errMsg) + private static bool TryParseArg(List args, out GenArgs options, out string errMsg) { var helpWriter = new StringWriter(); var parser = new Parser(ps => @@ -106,19 +112,19 @@ namespace Luban.Job.Cfg if (parseResult.Tag == ParserResultType.NotParsed) { errMsg = helpWriter.ToString(); - result = null; + options = null; return false; } else { - result = (parseResult as Parsed).Value; + options = (parseResult as Parsed).Value; errMsg = null; - string inputDataDir = result.InputDataDir; - string outputCodeDir = result.OutputCodeDir; - string outputDataDir = result.OutputDataDir; + string inputDataDir = options.InputDataDir; + string outputCodeDir = options.OutputCodeDir; + string outputDataDir = options.OutputDataDir; - var genTypes = result.GenType.Split(',').Select(s => s.Trim()).ToList(); + var genTypes = options.GenType.Split(',').Select(s => s.Trim()).ToList(); if (genTypes.Any(t => t.StartsWith("code_", StringComparison.Ordinal)) && string.IsNullOrWhiteSpace(outputCodeDir)) { @@ -137,24 +143,30 @@ namespace Luban.Job.Cfg errMsg = "--outputdatadir missing"; return false; } - if (genTypes.Contains("data_resources") && string.IsNullOrWhiteSpace(result.OutputDataResourceListFile)) + if (genTypes.Contains("data_resources") && string.IsNullOrWhiteSpace(options.OutputDataResourceListFile)) { errMsg = "--output_data_resource_list_file missing"; return false; } - if (genTypes.Contains("data_json_monolithic") && string.IsNullOrWhiteSpace(result.OutputDataJsonMonolithicFile)) + if (genTypes.Contains("data_json_monolithic") && string.IsNullOrWhiteSpace(options.OutputDataJsonMonolithicFile)) { errMsg = "--output_data_json_monolithic_file missing"; return false; } - if (string.IsNullOrWhiteSpace(result.InputTextTableFiles) ^ string.IsNullOrWhiteSpace(result.OutputNotConvertTextFile)) + if (string.IsNullOrWhiteSpace(options.InputTextTableFiles) ^ string.IsNullOrWhiteSpace(options.OutputNotConvertTextFile)) { errMsg = "--input_l10n_text_files must be provided with --output_l10n_not_converted_text_file"; return false; } } + if (string.IsNullOrWhiteSpace(options.BranchName) ^ string.IsNullOrWhiteSpace(options.BranchInputDataDir)) + { + errMsg = "--branch must be provided with --branch_input_data_dir"; + return false; + } + return true; } } @@ -197,7 +209,7 @@ namespace Luban.Job.Cfg var ass = new DefAssembly(timeZoneInfo); - ass.Load(args.Service, rawDefines, agent); + ass.Load(args.Service, args.BranchName, rawDefines, agent); var targetService = ass.CfgTargetService; @@ -216,7 +228,7 @@ namespace Luban.Job.Cfg hasLoadCfgData = true; var timer = new ProfileTimer(); timer.StartPhase("load config data"); - await DataLoaderUtil.LoadCfgDataAsync(agent, ass, args.InputDataDir, args.ExportTestData); + await DataLoaderUtil.LoadCfgDataAsync(agent, ass, args.InputDataDir, args.BranchName, args.BranchInputDataDir, args.ExportTestData); timer.EndPhaseAndLog(); if (needL10NTextConvert) diff --git a/src/Luban.Job.Cfg/Source/RawDefs/Branch.cs b/src/Luban.Job.Cfg/Source/RawDefs/Branch.cs new file mode 100644 index 0000000..9cfc04c --- /dev/null +++ b/src/Luban.Job.Cfg/Source/RawDefs/Branch.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Luban.Job.Cfg.RawDefs +{ + public class Branch + { + public string Name { get; } + + public Branch(string name) + { + Name = name; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/RawDefs/CfgBean.cs b/src/Luban.Job.Cfg/Source/RawDefs/CfgBean.cs index 9790d7f..ab6b176 100644 --- a/src/Luban.Job.Cfg/Source/RawDefs/CfgBean.cs +++ b/src/Luban.Job.Cfg/Source/RawDefs/CfgBean.cs @@ -1,6 +1,6 @@ using Luban.Job.Common.RawDefs; -namespace Luban.Config.Common.RawDefs +namespace Luban.Job.Cfg.RawDefs { public class CfgBean : Bean { diff --git a/src/Luban.Job.Cfg/Source/RawDefs/CfgField.cs b/src/Luban.Job.Cfg/Source/RawDefs/CfgField.cs index a6014e3..4237334 100644 --- a/src/Luban.Job.Cfg/Source/RawDefs/CfgField.cs +++ b/src/Luban.Job.Cfg/Source/RawDefs/CfgField.cs @@ -1,7 +1,7 @@ using Luban.Job.Common.RawDefs; using System.Collections.Generic; -namespace Luban.Config.Common.RawDefs +namespace Luban.Job.Cfg.RawDefs { public class Validator diff --git a/src/Luban.Job.Cfg/Source/RawDefs/Defines.cs b/src/Luban.Job.Cfg/Source/RawDefs/Defines.cs index 4b4ed22..4760f1c 100644 --- a/src/Luban.Job.Cfg/Source/RawDefs/Defines.cs +++ b/src/Luban.Job.Cfg/Source/RawDefs/Defines.cs @@ -1,12 +1,15 @@ +using Luban.Job.Cfg.RawDefs; using Luban.Job.Common.RawDefs; using System.Collections.Generic; -namespace Luban.Config.Common.RawDefs +namespace Luban.Job.Cfg.RawDefs { public class Defines { public string TopModule { get; set; } = ""; + public List Branches { get; set; } = new List(); + public List Beans { get; set; } = new List(); public List Consts { get; set; } = new List(); diff --git a/src/Luban.Job.Cfg/Source/RawDefs/Group.cs b/src/Luban.Job.Cfg/Source/RawDefs/Group.cs index bd63340..30ceb44 100644 --- a/src/Luban.Job.Cfg/Source/RawDefs/Group.cs +++ b/src/Luban.Job.Cfg/Source/RawDefs/Group.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Luban.Config.Common.RawDefs +namespace Luban.Job.Cfg.RawDefs { public class Group { diff --git a/src/Luban.Job.Cfg/Source/RawDefs/Service.cs b/src/Luban.Job.Cfg/Source/RawDefs/Service.cs index 59bd7c9..3054710 100644 --- a/src/Luban.Job.Cfg/Source/RawDefs/Service.cs +++ b/src/Luban.Job.Cfg/Source/RawDefs/Service.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Luban.Config.Common.RawDefs +namespace Luban.Job.Cfg.RawDefs { public class Service { diff --git a/src/Luban.Job.Cfg/Source/RawDefs/Table.cs b/src/Luban.Job.Cfg/Source/RawDefs/Table.cs index 038547e..fe7ef88 100644 --- a/src/Luban.Job.Cfg/Source/RawDefs/Table.cs +++ b/src/Luban.Job.Cfg/Source/RawDefs/Table.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Luban.Config.Common.RawDefs +namespace Luban.Job.Cfg.RawDefs { public enum ETableMode { @@ -35,5 +35,7 @@ namespace Luban.Config.Common.RawDefs public List Groups { get; set; } = new List(); public List InputFiles { get; set; } = new List(); + + public Dictionary> BranchInputFiles { get; set; } = new Dictionary>(); } } diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/DeepCompareTypeDefine.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/DeepCompareTypeDefine.cs index 0c49151..561ea8d 100644 --- a/src/Luban.Job.Cfg/Source/TypeVisitors/DeepCompareTypeDefine.cs +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/DeepCompareTypeDefine.cs @@ -1,5 +1,5 @@ -using Luban.Config.Common.RawDefs; using Luban.Job.Cfg.Defs; +using Luban.Job.Cfg.RawDefs; using Luban.Job.Common.Defs; using Luban.Job.Common.Types; using Luban.Job.Common.TypeVisitors; diff --git a/src/Luban.Job.Cfg/Source/Utils/DataExporterUtil.cs b/src/Luban.Job.Cfg/Source/Utils/DataExporterUtil.cs index a14efbf..3b91cb5 100644 --- a/src/Luban.Job.Cfg/Source/Utils/DataExporterUtil.cs +++ b/src/Luban.Job.Cfg/Source/Utils/DataExporterUtil.cs @@ -1,5 +1,4 @@ using Bright.Serialization; -using Luban.Config.Common.RawDefs; using Luban.Job.Cfg.Datas; using Luban.Job.Cfg.DataVisitors; using Luban.Job.Cfg.Defs; diff --git a/src/Luban.Job.Cfg/Source/Utils/DataLoaderUtil.cs b/src/Luban.Job.Cfg/Source/Utils/DataLoaderUtil.cs index ea72aa6..14b1fff 100644 --- a/src/Luban.Job.Cfg/Source/Utils/DataLoaderUtil.cs +++ b/src/Luban.Job.Cfg/Source/Utils/DataLoaderUtil.cs @@ -71,16 +71,14 @@ namespace Luban.Job.Cfg.Utils // return CollectInputFilesAsync(agent, table.InputFiles, dataDir) //} - public static async Task LoadTableAsync(RemoteAgent agent, DefTable table, string dataDir, bool exportTestData) + public static async Task GenerateLoadRecordFromFileTasksAsync(RemoteAgent agent, DefTable table, string dataDir, List inputFiles2, bool exportTestData, List>> tasks) { - var tasks = new List>>(); - - var inputFiles = await CollectInputFilesAsync(agent, table.InputFiles, dataDir); + var inputFileInfos = await CollectInputFilesAsync(agent, inputFiles2, dataDir); // check cache (table, exporttestdata) -> (list, List) // (md5, sheetName,exportTestData) -> (value_type, List) - foreach (var file in inputFiles) + foreach (var file in inputFileInfos) { var actualFile = file.ActualFile; //s_logger.Info("== get input file:{file} actualFile:{actual}", file, actualFile); @@ -103,21 +101,52 @@ namespace Luban.Job.Cfg.Utils return res; })); } - - var records = new List(tasks.Count); - foreach (var task in tasks) - { - records.AddRange(await task); - } - - s_logger.Trace("== load recors. count:{count}", records.Count); - - table.Assembly.AddDataTable(table, records); - - s_logger.Trace("table:{name} record num:{num}", table.FullName, records.Count); } - public static async Task LoadCfgDataAsync(RemoteAgent agent, DefAssembly ass, string dataDir, bool exportTestData) + public static async Task LoadTableAsync(RemoteAgent agent, DefTable table, string dataDir, string branchName, string branchDataDir, bool exportTestData) + { + var mainLoadTasks = new List>>(); + var mainGenerateTask = GenerateLoadRecordFromFileTasksAsync(agent, table, dataDir, table.InputFiles, exportTestData, mainLoadTasks); + + var branchLoadTasks = new List>>(); + + Task branchGenerateTask = null; + if (!string.IsNullOrWhiteSpace(branchName)) + { + var branchInputFiles = table.GetBranchInputFiles(branchName); + if (branchInputFiles != null) + { + branchGenerateTask = GenerateLoadRecordFromFileTasksAsync(agent, table, branchDataDir, branchInputFiles, exportTestData, branchLoadTasks); + } + } + + 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 branchRecords = null; + if (branchGenerateTask != null) + { + branchRecords = new List(64); + await branchGenerateTask; + foreach (var task in branchLoadTasks) + { + branchRecords.AddRange(await task); + } + s_logger.Trace("== load branch records. count:{count}", branchRecords.Count); + } + + table.Assembly.AddDataTable(table, mainRecords, branchRecords); + + s_logger.Trace("table:{name} record num:{num}", table.FullName, mainRecords.Count); + } + + public static async Task LoadCfgDataAsync(RemoteAgent agent, DefAssembly ass, string dataDir, string branchName, string branchDataDir, bool exportTestData) { var ctx = agent; List exportTables = ass.Types.Values.Where(t => t is DefTable ct && ct.NeedExport).Select(t => (DefTable)t).ToList(); @@ -130,7 +159,7 @@ namespace Luban.Job.Cfg.Utils genDataTasks.Add(Task.Run(async () => { long beginTime = TimeUtil.NowMillis; - await LoadTableAsync(agent, c, dataDir, exportTestData); + await LoadTableAsync(agent, c, dataDir, branchName, branchDataDir, exportTestData); long endTime = TimeUtil.NowMillis; if (endTime - beginTime > 100) { diff --git a/src/Luban.Job.Cfg/Source/ValidatorContext.cs b/src/Luban.Job.Cfg/Source/ValidatorContext.cs index ba6152a..55add5d 100644 --- a/src/Luban.Job.Cfg/Source/ValidatorContext.cs +++ b/src/Luban.Job.Cfg/Source/ValidatorContext.cs @@ -1,8 +1,8 @@ using Luban.Common.Utils; -using Luban.Config.Common.RawDefs; using Luban.Job.Cfg.Datas; using Luban.Job.Cfg.DataVisitors; using Luban.Job.Cfg.Defs; +using Luban.Job.Cfg.RawDefs; using Luban.Job.Cfg.Utils; using Luban.Job.Cfg.Validators; using System; @@ -27,6 +27,8 @@ namespace Luban.Job.Cfg public class ValidatorContext { + private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger(); + [ThreadStatic] private static ValidatorVisitor t_visitor; @@ -67,8 +69,7 @@ namespace Luban.Job.Cfg { tasks.Add(Task.Run(() => { - var records = t.Assembly.GetTableDataList(t); - ValidateTableModeIndex(t, records); + ValidateTableModeIndex(t); })); } await Task.WhenAll(tasks); @@ -171,58 +172,134 @@ namespace Luban.Job.Cfg } } - private void ValidateTableModeIndex(DefTable table, List records) + private void ValidateTableModeIndex(DefTable table) { - var recordMap = new Dictionary(); + var tableDataInfo = Assembly.GetTableDataInfo(table); + + List mainRecords = tableDataInfo.MainRecords; + List branchRecords = tableDataInfo.BranchRecords; + + // 这么大费周张是为了保证被覆盖的id仍然保持原来的顺序,而不是出现在最后 + int index = 0; + foreach (var r in mainRecords) + { + r.Index = index++; + } + if (branchRecords != null) + { + foreach (var r in branchRecords) + { + r.Index = index++; + } + } + + var mainRecordMap = new Dictionary(); switch (table.Mode) { case ETableMode.ONE: { - if (records.Count != 1) + if (mainRecords.Count != 1) { - throw new Exception($"配置表 {table.FullName} 是单值表 mode=one,但数据个数:{records.Count} != 1"); + throw new Exception($"配置表 {table.FullName} 是单值表 mode=one,但主文件数据个数:{mainRecords.Count} != 1"); + } + if (branchRecords != null && branchRecords.Count != 1) + { + throw new Exception($"配置表 {table.FullName} 是单值表 mode=one,但分支文件数据个数:{branchRecords.Count} != 1"); + } + if (branchRecords != null) + { + mainRecords[0] = branchRecords[0]; } break; } case ETableMode.MAP: { - foreach (Record r in records) + foreach (Record r in mainRecords) { DType key = r.Data.Fields[table.IndexFieldIdIndex]; - if (!recordMap.TryAdd(key, r)) + if (!mainRecordMap.TryAdd(key, r)) { - throw new Exception($@"配置表 {table.FullName} 主键字段:{table.Index} 主键值:{key} 重复. + throw new Exception($@"配置表 {table.FullName} 主文件 主键字段:{table.Index} 主键值:{key} 重复. 记录1 来自文件:{r.Source} - 记录2 来自文件:{recordMap[key].Source} + 记录2 来自文件:{mainRecordMap[key].Source} "); } - + } + if (branchRecords != null) + { + var branchRecordMap = new Dictionary(); + foreach (Record r in branchRecords) + { + DType key = r.Data.Fields[table.IndexFieldIdIndex]; + if (!branchRecordMap.TryAdd(key, r)) + { + throw new Exception($@"配置表 {table.FullName} 分支文件 主键字段:{table.Index} 主键值:{key} 重复. + 记录1 来自文件:{r.Source} + 记录2 来自文件:{branchRecordMap[key].Source} +"); + } + if (mainRecordMap.TryGetValue(key, out var old)) + { + s_logger.Debug("配置表 {} 分支文件 主键:{} 覆盖 主文件记录", table.FullName, key); + mainRecords[old.Index] = r; + } + mainRecordMap[key] = r; + } } break; } case ETableMode.BMAP: { - var twoKeyMap = new Dictionary<(DType, DType), Record>(); - foreach (Record r in records) + var mainTwoKeyMap = new Dictionary<(DType, DType), Record>(); + foreach (Record r in mainRecords) { DType key1 = r.Data.Fields[table.IndexFieldIdIndex1]; DType key2 = r.Data.Fields[table.IndexFieldIdIndex2]; - if (!twoKeyMap.TryAdd((key1, key2), r)) + if (!mainTwoKeyMap.TryAdd((key1, key2), r)) { - throw new Exception($@"配置表 {table.FullName} 主键字段:{table.Index} 主键值:({key1},{key2})重复. + throw new Exception($@"配置表 {table.FullName} 主文件 主键字段:{table.Index} 主键值:({key1},{key2})重复. 记录1 来自文件:{r.Source} - 记录2 来自文件:{twoKeyMap[(key1, key2)].Source} + 记录2 来自文件:{mainTwoKeyMap[(key1, key2)].Source} "); } // 目前不支持 双key索引检查,但支持主key索引检查. // 所以至少塞入一个,让ref检查能通过 - recordMap[key1] = r; + mainRecordMap[key1] = r; } + + if (branchRecords != null) + { + var branchTwoKeyMap = new Dictionary<(DType, DType), Record>(); + foreach (Record r in branchRecords) + { + DType key1 = r.Data.Fields[table.IndexFieldIdIndex1]; + DType key2 = r.Data.Fields[table.IndexFieldIdIndex2]; + if (!branchTwoKeyMap.TryAdd((key1, key2), r)) + { + throw new Exception($@"配置表 {table.FullName} 分支文件 主键字段:{table.Index} 主键值:({key1},{key2})重复. + 记录1 来自文件:{r.Source} + 记录2 来自文件:{branchTwoKeyMap[(key1, key2)].Source} +"); + } + if (mainTwoKeyMap.TryGetValue((key1, key2), out var old)) + { + s_logger.Debug("配置表 {} 分支文件 主键:({},{}) 覆盖 主文件记录", table.FullName, key1, key2); + mainRecords[old.Index] = r; + } + mainTwoKeyMap[(key1, key2)] = r; + // 目前不支持 双key索引检查,但支持主key索引检查. + // 所以至少塞入一个,让ref检查能通过 + mainRecordMap[key1] = r; + } + } + + break; } } - table.Assembly.SetDataTableMap(table, recordMap); + tableDataInfo.FinalRecords = mainRecords; + tableDataInfo.FinalRecordMap = mainRecordMap; } } } diff --git a/src/Luban.Job.Cfg/Source/Validators/RefValidator.cs b/src/Luban.Job.Cfg/Source/Validators/RefValidator.cs index 2a8b307..eacc928 100644 --- a/src/Luban.Job.Cfg/Source/Validators/RefValidator.cs +++ b/src/Luban.Job.Cfg/Source/Validators/RefValidator.cs @@ -53,7 +53,7 @@ namespace Luban.Job.Cfg.Validators return; } DefTable ct = assembly.GetCfgTable(actualTable); - var recordMap = assembly.GetTableDataMap(ct); + var recordMap = assembly.GetTableDataInfo(ct).FinalRecordMap; if (/*recordMap != null &&*/ recordMap.ContainsKey(key)) { return; diff --git a/src/Luban.Job.Cfg/Source/Validators/ValidatorFactory.cs b/src/Luban.Job.Cfg/Source/Validators/ValidatorFactory.cs index 4efe0cc..e6a13fc 100644 --- a/src/Luban.Job.Cfg/Source/Validators/ValidatorFactory.cs +++ b/src/Luban.Job.Cfg/Source/Validators/ValidatorFactory.cs @@ -1,4 +1,4 @@ -using Luban.Config.Common.RawDefs; +using Luban.Job.Cfg.RawDefs; using System; using System.Collections.Generic; using System.Linq; diff --git a/src/Luban.Job.Cfg/Source/l10n/TextTable.cs b/src/Luban.Job.Cfg/Source/l10n/TextTable.cs index 65ee6b9..ca8cde8 100644 --- a/src/Luban.Job.Cfg/Source/l10n/TextTable.cs +++ b/src/Luban.Job.Cfg/Source/l10n/TextTable.cs @@ -1,7 +1,7 @@ -using Luban.Config.Common.RawDefs; -using Luban.Job.Cfg.Datas; +using Luban.Job.Cfg.Datas; using Luban.Job.Cfg.DataVisitors; using Luban.Job.Cfg.Defs; +using Luban.Job.Cfg.RawDefs; using Luban.Job.Cfg.Utils; using Luban.Job.Common.Types; using System;