using Bright.Collections; using Luban.Job.Cfg.Datas; using Luban.Job.Cfg.DataSources; #if !LUBAN_LITE using Luban.Job.Cfg.l10n; #endif using Luban.Job.Cfg.RawDefs; using Luban.Job.Cfg.TypeVisitors; using Luban.Job.Common.Defs; using Luban.Job.Common.Types; 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 DefTable Table { get; } public List MainRecords { get; } public List PatchRecords { get; } public List FinalRecords { get; set; } public Dictionary FinalRecordMap { get; set; } public TableDataInfo(DefTable table, List mainRecords, List patchRecords) { Table = table; 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 bool OutputCompactJson { get; set; } 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(); #if !LUBAN_LITE public RawTextTable RawTextTable { get; } = new RawTextTable(); public TextTable ExportTextTable { get; private set; } public NotConvertTextSet NotConvertTextSet { get; private set; } public bool NeedL10nTextTranslate => ExportTextTable != null; public void InitL10n(string textValueFieldName) { ExportTextTable = new TextTable(this, textValueFieldName); NotConvertTextSet = new NotConvertTextSet(); } #endif 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(table, 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 static List ToSortByKeyDataList(DefTable table, List originRecords) { var sortedRecords = new List(originRecords); DefField keyField = table.IndexField; if (keyField != null && (keyField.CType is TInt || keyField.CType is TLong)) { string keyFieldName = keyField.Name; sortedRecords.Sort((a, b) => { DType keya = a.Data.GetField(keyFieldName); DType keyb = b.Data.GetField(keyFieldName); switch (keya) { case DInt ai: return ai.Value.CompareTo((keyb as DInt).Value); case DLong al: return al.Value.CompareTo((keyb as DLong).Value); default: throw new NotSupportedException(); } }); } return sortedRecords; } 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; } } } } }