diff --git a/README.md b/README.md index cb5f0d6..20e0f3c 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ Luban适合有以下需求的开发者: - 灵活的数据源定义。一个表可以来自多个文件或者一个文件内定义多个表或者一个目录下所有文件甚至来自云表格,以及以上的组合 - 支持表与字段级别分组。可以选择性地导出客户端或者服务器所用的表及字段 - 多种导出数据格式支持。支持binary、json、lua 等导出数据格式 +- 支持自定义数据模板。可以用模板文件定制导出格式。 - 支持数据标签。 可以选择导出符合要求的数据,发布正式数据时策划不必手动注释掉那些测试数据了 - 强大的数据校验能力。支持内建数据格式检查;支持ref表引用检查(策划不用担心填错id);支持path资源检查(策划不用担心填错资源路径);支持range检查 - 支持常量别名。策划不必再为诸如 升级丹 这样的道具手写具体道具id了 @@ -945,6 +946,66 @@ k15: ``` +### 自定义导出数据格式 + +支持使用scriban模板文件定制导出数据格式。 + +lua数据模板 定义 + +``` +// {{table.name}} + +{{for d in datas}} + // {{d.impl_type.full_name}} + {{~i = 0~}} + {{~for f in d.fields~}} + {{~if f ~}} + // {{d.impl_type.hierarchy_export_fields[i].name}} = {{f.value}} + {{~end~}} + {{i = i + 1}} + {{~end~}} +{{end}} +``` + +输出数据 + +``` +// TbItem + + + // item.Item + // id = 1 + + // name = 钻石 + + // major_type = 1 + + // minor_type = 101 + + // max_pile_num = 9999999 + + // quality = 0 + + // icon = /Game/UI/UIText/UI_TestIcon_3.UI_TestIcon_3 + + + // item.Item + // id = 2 + + // name = 金币 + + // major_type = 1 + + // minor_type = 102 + + // max_pile_num = 9999999 + + // quality = 0 + + // icon = /Game/UI/UIText/UI_TestIcon_1.UI_TestIcon_1 + +``` + ----- ## 路线图 diff --git a/src/Luban.Job.Cfg/Source/DataExporters/LuaExportor.cs b/src/Luban.Job.Cfg/Source/DataExporters/LuaExportor.cs index f7f7679..c9af468 100644 --- a/src/Luban.Job.Cfg/Source/DataExporters/LuaExportor.cs +++ b/src/Luban.Job.Cfg/Source/DataExporters/LuaExportor.cs @@ -1,6 +1,7 @@ using Luban.Job.Cfg.Datas; using Luban.Job.Cfg.DataVisitors; using Luban.Job.Cfg.Defs; +using Luban.Job.Cfg.Utils; using System; using System.Collections.Generic; using System.Linq; @@ -100,14 +101,9 @@ namespace Luban.Job.Cfg.DataExporters line.Append(type.Value); } - private string EscapeString(string s) - { - return s.Replace("\\", "\\\\").Replace("'", "\\'"); - } - public void Accept(DString type, DefAssembly ass, StringBuilder line) { - line.Append('\'').Append(EscapeString(type.Value)).Append('\''); + line.Append('\'').Append(DataUtil.EscapeString(type.Value)).Append('\''); } public void Accept(DBytes type, DefAssembly ass, StringBuilder line) @@ -117,7 +113,7 @@ namespace Luban.Job.Cfg.DataExporters public void Accept(DText type, DefAssembly ass, StringBuilder line) { - line.Append($"{{{DText.KEY_NAME}='{type.Key}',{DText.TEXT_NAME}='{EscapeString(type.GetText(ass.ExportTextTable, ass.NotConvertTextSet))}'}}"); + line.Append($"{{{DText.KEY_NAME}='{type.Key}',{DText.TEXT_NAME}='{DataUtil.EscapeString(type.GetText(ass.ExportTextTable, ass.NotConvertTextSet))}'}}"); } public void Accept(DBean type, DefAssembly ass, StringBuilder line) diff --git a/src/Luban.Job.Cfg/Source/DataVisitors/ToJsonStringVisitor.cs b/src/Luban.Job.Cfg/Source/DataVisitors/ToJsonStringVisitor.cs new file mode 100644 index 0000000..afb9fe4 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/DataVisitors/ToJsonStringVisitor.cs @@ -0,0 +1,201 @@ +using Luban.Job.Cfg.Datas; +using Luban.Job.Cfg.Defs; +using Luban.Job.Cfg.Utils; +using System.Collections.Generic; +using System.Text; + +namespace Luban.Job.Cfg.DataVisitors +{ + class ToJsonStringVisitor : IDataFuncVisitor + { + public static ToJsonStringVisitor Ins { get; } = new(); + + public string Accept(DBool type) + { + return type.Value ? "true" : "false"; + } + + public string Accept(DByte type) + { + return type.Value.ToString(); + } + + public string Accept(DShort type) + { + return type.Value.ToString(); + } + + public string Accept(DFshort type) + { + return type.Value.ToString(); + } + + public string Accept(DInt type) + { + return type.Value.ToString(); + } + + public string Accept(DFint type) + { + return type.Value.ToString(); + } + + public string Accept(DLong type) + { + return type.Value.ToString(); + } + + public string Accept(DFlong type) + { + return type.Value.ToString(); + } + + public string Accept(DFloat type) + { + return type.Value.ToString(); + } + + public string Accept(DDouble type) + { + return type.Value.ToString(); + } + + public string Accept(DEnum type) + { + return type.Value.ToString(); + } + + public string Accept(DString type) + { + return "\"" + DataUtil.EscapeString(type.Value) + "\""; + } + + public string Accept(DBytes type) + { + throw new System.NotSupportedException(); + } + + public virtual string Accept(DText type) + { + var ass = DefAssembly.LocalAssebmly as DefAssembly; + return $"{{\"{DText.KEY_NAME}\":\"{type.Key}\",\"{DText.TEXT_NAME}\":\"{DataUtil.EscapeString(type.GetText(ass.ExportTextTable, ass.NotConvertTextSet))}\"}}"; + } + + public virtual string Accept(DBean type) + { + var x = new StringBuilder(); + var bean = type.ImplType; + if (bean.IsAbstractType) + { + x.Append($"{{ \"_name\":\"{type.ImplType.Name}\","); + } + else + { + x.Append('{'); + } + + int index = 0; + foreach (var f in type.Fields) + { + if (index >= 1) + { + x.Append(','); + } + var defField = type.ImplType.HierarchyFields[index++]; + x.Append('\"').Append(defField.Name).Append('\"').Append(':'); + if (f != null) + { + x.Append(f.Apply(this)); + } + else + { + x.Append("null"); + } + } + x.Append('}'); + return x.ToString(); + } + + + protected virtual void Append(List datas, StringBuilder x) + { + x.Append('['); + int index = 0; + foreach (var e in datas) + { + if (index > 0) + { + x.Append(','); + } + ++index; + x.Append(e.Apply(this)); + } + x.Append(']'); + } + + public string Accept(DArray type) + { + var x = new StringBuilder(); + Append(type.Datas, x); + return x.ToString(); + } + + public string Accept(DList type) + { + var x = new StringBuilder(); + Append(type.Datas, x); + return x.ToString(); + } + + public string Accept(DSet type) + { + var x = new StringBuilder(); + Append(type.Datas, x); + return x.ToString(); + } + + public virtual string Accept(DMap type) + { + var x = new StringBuilder(); + x.Append('{'); + int index = 0; + foreach (var e in type.Datas) + { + if (index > 0) + { + x.Append(','); + } + ++index; + x.Append('"').Append(e.Key.ToString()).Append('"'); + x.Append(':'); + x.Append(e.Value.Apply(this)); + } + x.Append('}'); + return x.ToString(); + } + + public virtual string Accept(DVector2 type) + { + var v = type.Value; + return $"{{\"x\":{v.X},\"y\":{v.Y}}}"; + } + + public virtual string Accept(DVector3 type) + { + var v = type.Value; + return $"{{\"x\":{v.X},\"y\":{v.Y},\"z\":{v.Z}}}"; + } + + public virtual string Accept(DVector4 type) + { + var v = type.Value; + return $"{{\"x\":{v.X},\"y\":{v.Y},\"z\":{v.Z},\"w\":{v.W}}}"; + } + + public string Accept(DDateTime type) + { + var ass = DefAssembly.LocalAssebmly as DefAssembly; + return type.GetUnixTime(ass.TimeZone).ToString(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/DataVisitors/ToLuaStringVisitor.cs b/src/Luban.Job.Cfg/Source/DataVisitors/ToLuaStringVisitor.cs new file mode 100644 index 0000000..1c3bf33 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/DataVisitors/ToLuaStringVisitor.cs @@ -0,0 +1,98 @@ +using Luban.Job.Cfg.Datas; +using Luban.Job.Cfg.Defs; +using Luban.Job.Cfg.Utils; +using System.Collections.Generic; +using System.Text; + +namespace Luban.Job.Cfg.DataVisitors +{ + class ToLuaStringVisitor : ToJsonStringVisitor + { + public static new ToLuaStringVisitor Ins { get; } = new(); + + public override string Accept(DText type) + { + var ass = DefAssembly.LocalAssebmly as DefAssembly; + return $"{{{DText.KEY_NAME}='{type.Key}',{DText.TEXT_NAME}='{DataUtil.EscapeString(type.GetText(ass.ExportTextTable, ass.NotConvertTextSet))}'}}"; + } + + public override string Accept(DBean type) + { + var x = new StringBuilder(); + var bean = type.ImplType; + if (bean.IsAbstractType) + { + x.Append($"{{ _name='{type.ImplType.Name}',"); + } + else + { + x.Append('{'); + } + + int index = 0; + foreach (var f in type.Fields) + { + var defField = type.ImplType.HierarchyFields[index++]; + x.Append(defField.Name).Append('='); + if (f != null) + { + x.Append(f.Apply(this)); + } + else + { + x.Append("nil"); + } + x.Append(','); + } + x.Append('}'); + return x.ToString(); + } + + + protected override void Append(List datas, StringBuilder x) + { + x.Append('{'); + foreach (var e in datas) + { + x.Append(e.Apply(this)); + x.Append(','); + } + x.Append('}'); + } + + public override string Accept(DMap type) + { + var x = new StringBuilder(); + x.Append('{'); + foreach (var e in type.Datas) + { + x.Append('['); + x.Append(e.Key.Apply(this)); + x.Append(']'); + x.Append('='); + x.Append(e.Value.Apply(this)); + x.Append(','); + } + x.Append('}'); + return x.ToString(); + } + + public override string Accept(DVector2 type) + { + var v = type.Value; + return $"{{x={v.X},y={v.Y}}}"; + } + + public override string Accept(DVector3 type) + { + var v = type.Value; + return $"{{x={v.X},y={v.Y},z={v.Z}}}"; + } + + public override string Accept(DVector4 type) + { + var v = type.Value; + return $"{{x={v.X},y={v.Y},z={v.Z},w={v.W}}}"; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/DataVisitors/ToStringVisitor.cs b/src/Luban.Job.Cfg/Source/DataVisitors/ToStringVisitor.cs index 37b4aa5..0994b45 100644 --- a/src/Luban.Job.Cfg/Source/DataVisitors/ToStringVisitor.cs +++ b/src/Luban.Job.Cfg/Source/DataVisitors/ToStringVisitor.cs @@ -10,7 +10,7 @@ namespace Luban.Job.Cfg.DataVisitors public void Accept(DBool type, StringBuilder x) { - x.Append(type.Value); + x.Append(type.Value ? "true" : "false"); } public void Accept(DByte type, StringBuilder x) diff --git a/src/Luban.Job.Cfg/Source/Datas/DArray.cs b/src/Luban.Job.Cfg/Source/Datas/DArray.cs index 31d3e31..7e68ae3 100644 --- a/src/Luban.Job.Cfg/Source/Datas/DArray.cs +++ b/src/Luban.Job.Cfg/Source/Datas/DArray.cs @@ -9,6 +9,8 @@ namespace Luban.Job.Cfg.Datas public TArray Type { get; } public List Datas { get; } + public bool IsArray => true; + public DArray(TArray type, List datas) { this.Type = type; diff --git a/src/Luban.Job.Cfg/Source/Datas/DBean.cs b/src/Luban.Job.Cfg/Source/Datas/DBean.cs index ee4bb0a..0d06792 100644 --- a/src/Luban.Job.Cfg/Source/Datas/DBean.cs +++ b/src/Luban.Job.Cfg/Source/Datas/DBean.cs @@ -12,6 +12,8 @@ namespace Luban.Job.Cfg.Datas public List Fields { get; } + public bool IsBean => true; + public DBean(DefBean defType, DefBean implType, List fields) { this.Type = defType; diff --git a/src/Luban.Job.Cfg/Source/Datas/DList.cs b/src/Luban.Job.Cfg/Source/Datas/DList.cs index e435825..1644c57 100644 --- a/src/Luban.Job.Cfg/Source/Datas/DList.cs +++ b/src/Luban.Job.Cfg/Source/Datas/DList.cs @@ -9,6 +9,8 @@ namespace Luban.Job.Cfg.Datas public TList Type { get; } public List Datas { get; } + public bool IsList => true; + public DList(TList type, List datas) { this.Type = type; diff --git a/src/Luban.Job.Cfg/Source/Datas/DMap.cs b/src/Luban.Job.Cfg/Source/Datas/DMap.cs index ac43607..5118f46 100644 --- a/src/Luban.Job.Cfg/Source/Datas/DMap.cs +++ b/src/Luban.Job.Cfg/Source/Datas/DMap.cs @@ -10,6 +10,8 @@ namespace Luban.Job.Cfg.Datas public TMap Type { get; } public Dictionary Datas { get; } + public bool IsMap => true; + public DMap(TMap type, Dictionary datas) { this.Type = type; diff --git a/src/Luban.Job.Cfg/Source/Datas/DSet.cs b/src/Luban.Job.Cfg/Source/Datas/DSet.cs index 464c668..c0ec347 100644 --- a/src/Luban.Job.Cfg/Source/Datas/DSet.cs +++ b/src/Luban.Job.Cfg/Source/Datas/DSet.cs @@ -10,6 +10,8 @@ namespace Luban.Job.Cfg.Datas public TSet Type { get; } public List Datas { get; } + public bool IsSet => true; + public DSet(TSet type, List datas) { this.Type = type; diff --git a/src/Luban.Job.Cfg/Source/Datas/DType.cs b/src/Luban.Job.Cfg/Source/Datas/DType.cs index 15f33dc..180764c 100644 --- a/src/Luban.Job.Cfg/Source/Datas/DType.cs +++ b/src/Luban.Job.Cfg/Source/Datas/DType.cs @@ -20,6 +20,10 @@ namespace Luban.Job.Cfg.Datas this.Apply(ToStringVisitor.Ins, s); return s.ToString(); } + + public string JsonValue => this.Apply(ToJsonStringVisitor.Ins); + + public string LuaValue => this.Apply(ToLuaStringVisitor.Ins); } public abstract class DType : DType diff --git a/src/Luban.Job.Cfg/Source/Defs/TTypeTemplateExtends.cs b/src/Luban.Job.Cfg/Source/Defs/TTypeTemplateExtends.cs index fea025e..c8cbaec 100644 --- a/src/Luban.Job.Cfg/Source/Defs/TTypeTemplateExtends.cs +++ b/src/Luban.Job.Cfg/Source/Defs/TTypeTemplateExtends.cs @@ -1,3 +1,4 @@ +using Luban.Job.Cfg.Datas; using Luban.Job.Cfg.TypeVisitors; using Luban.Job.Common.Defs; using Luban.Job.Common.Types; @@ -8,6 +9,20 @@ namespace Luban.Job.Cfg.Defs { class TTypeTemplateExtends : TTypeTemplateCommonExtends { + public static DType GetField(DBean bean, string fieldName) + { + int index = 0; + foreach (var f in bean.ImplType.HierarchyExportFields) + { + if (f.Name == fieldName) + { + return bean.Fields[index]; + } + ++index; + } + return null; + } + public static string CsDefineTextKeyField(DefField field) { return $"string {field.GetTextKeyName(field.CsStyleName)}"; diff --git a/src/Luban.Job.Cfg/Source/Utils/DataUtil.cs b/src/Luban.Job.Cfg/Source/Utils/DataUtil.cs index 624187e..7123877 100644 --- a/src/Luban.Job.Cfg/Source/Utils/DataUtil.cs +++ b/src/Luban.Job.Cfg/Source/Utils/DataUtil.cs @@ -78,6 +78,16 @@ namespace Luban.Job.Cfg.Utils } } + public static string EscapeString(string s) + { + return s.Replace("\\", "\\\\").Replace("'", "\\'"); + } + + public static string EscapeStringWithQuote(string s) + { + return "'" + s.Replace("\\", "\\\\").Replace("'", "\\'") + "'"; + } + public static (string Key, string Text) ExtractText(string rawKeyAndText) { string[] keyAndText = rawKeyAndText.Split('|'); diff --git a/src/Luban.Job.Common/Luban.Job.Common.csproj b/src/Luban.Job.Common/Luban.Job.Common.csproj index ec9a2ad..afca7bf 100644 --- a/src/Luban.Job.Common/Luban.Job.Common.csproj +++ b/src/Luban.Job.Common/Luban.Job.Common.csproj @@ -12,7 +12,7 @@ - + diff --git a/src/Luban.Server/Luban.Server.csproj b/src/Luban.Server/Luban.Server.csproj index 710d7c1..3348f16 100644 --- a/src/Luban.Server/Luban.Server.csproj +++ b/src/Luban.Server/Luban.Server.csproj @@ -24,7 +24,6 @@ -