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 DefAssembly : DefAssemblyBase { private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger(); public static new 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 string TableManagerName => CfgTargetService.Manager; 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 Dictionary _refGroups = new(); private readonly ConcurrentDictionary _recordsByTables = new(); public Dictionary CfgTablesByName = new(); public Dictionary CfgTablesByFullName { 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 (!CfgTablesByFullName.TryAdd(table.FullName, table)) { throw new Exception($"table:'{table.FullName}' duplicated"); } if (!CfgTablesByName.TryAdd(table.Name, table)) { throw new Exception($"table:'{table.FullName} 与 table:'{CfgTablesByName[table.Name].FullName}' 的表名重复(不同模块下也不允许定义同名表,将来可能会放开限制)"); } } public DefTable GetCfgTable(string name) { return CfgTablesByFullName.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 GetAllTables() { return Types.Values.Where(t => t is DefTable).Cast().ToList(); } 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(); } private void AddRefGroup(RefGroup g) { if (_refGroups.ContainsKey(g.Name)) { throw new Exception($"refgroup:{g.Name} 重复"); } _refGroups.Add(g.Name, new DefRefGroup(g)); } public DefRefGroup GetRefGroup(string groupName) { return _refGroups.TryGetValue(groupName, out var refGroup) ? refGroup : null; } public void Load(Defines defines, IAgent agent, GenArgs args) { LoadCommon(defines, agent, args); OutputCompactJson = args.OutputDataCompactJson; SupportDatetimeType = true; CfgTargetService = defines.Services.Find(s => s.Name == args.Service); if (CfgTargetService == null) { throw new ArgumentException($"service:{args.Service} 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 g in defines.RefGroups) { AddRefGroup(g); } 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; } } } } }