From 7ea907cc5b39d2b466bbe67628494df6f3ceac35 Mon Sep 17 00:00:00 2001 From: walon Date: Fri, 27 Aug 2021 16:35:22 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E7=89=B9=E6=80=A7=E3=80=91=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=95=B0=E6=8D=AE=E6=A8=A1=E6=9D=BF=E3=80=82=E5=85=81?= =?UTF-8?q?=E8=AE=B8=E8=87=AA=E5=AE=9A=E4=B9=89=E7=94=9F=E6=88=90=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=EF=BC=88=E8=87=AA=E5=AE=9A=E4=B9=89=E7=94=9F=E6=88=90?= =?UTF-8?q?=E7=9A=84=E6=95=B0=E6=8D=AE=E4=B8=8D=E8=83=BD=E8=A2=AB=E6=A0=87?= =?UTF-8?q?=E5=87=86=E7=9A=84=E7=94=9F=E6=88=90=E4=BB=A3=E7=A0=81=E8=AF=86?= =?UTF-8?q?=E5=88=AB=EF=BC=8C=E5=9B=A0=E6=AD=A4=E5=8F=AA=E8=83=BD=E7=94=A8?= =?UTF-8?q?=E4=BA=8E=E8=87=AA=E5=AE=9A=E4=B9=89=E5=8A=A0=E8=BD=BD=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 16 ++++ docs/data_excel.md | 87 +++++++++++------- docs/images/adv/def_50.png | Bin 0 -> 7468 bytes .../Source/Generate/DataScatterRender.cs | 3 + src/Luban.Job.Cfg/Source/RenderExtension.cs | 20 ++++ .../Source/Utils/DataExporterUtil.cs | 7 ++ .../Source/Utils/StringTemplateUtil.cs | 30 +++++- .../Properties/launchSettings.json | 4 + 8 files changed, 130 insertions(+), 37 deletions(-) create mode 100644 docs/images/adv/def_50.png diff --git a/README.md b/README.md index 921e9fa..cb5f0d6 100644 --- a/README.md +++ b/README.md @@ -591,6 +591,22 @@ luban同时支持两种级别的分组: ![tag](docs/images/examples/tag_01.png) +## excel 列 默认值 + +该特性只对excel格式文件有效。当单元格为空时,该字段使用默认值。 + +```xml + + + + + + + +``` + +![default](docs/images/adv/def_50.png) + ### 常量别名 游戏里经常会出现一些常用的类似枚举的值,比如说 升级丹的 id,在很多地方都要填,如果直接它的道具 id,既不直观,也容易出错。 Luban 支持常量替换。对于需要常量替换的字段,添加 convert=”枚举类”。 如果填写的值是 枚举名或者别名,则替换为 相应的整数。否则 按照整数解析。 diff --git a/docs/data_excel.md b/docs/data_excel.md index 2d967f5..d6bb91b 100644 --- a/docs/data_excel.md +++ b/docs/data_excel.md @@ -89,6 +89,46 @@ text的key和text字段都是string类型,因此在连续单元格或者sep产 ![数据](images/examples/ex_i10n_2.png) +## datetime 类型 + +- 时间是常用的数据类型。Luban 特地提供了支持。 + 有两种形式,一种以纯字符串的方式填写。 + - 以纯字符串方式填写 + 填写格式为 以下 4 种。 + - yyyy-mm-dd hh:mm:ss 如 1999-08-08 01:30:29 + - yyyy-mm-dd hh:mm 如 2000-08-07 07:40 + - yyyy-mm-dd hh 如 2001-09-05 07 + - yyyy-mm-dd 如 2003-04-05 + - 以 excel内置的时间格式填写 + +- 为 Item 新增一个 失效时间字段 expire_time 。 +- [定义](images/adv/def_38.png) + ``` xml + + ... + + + ``` +- 配置 + 图中红框中第一个时间以 excel的时间格式填写 + 剩下第2,3,4个时间以 纯字符串形式填写 + + ![如图](images/adv/def_39.png) + +## 可空变量 + +- 有时候会有一种变量,我们希望它 功能生效时填一个有效值,功能不生效里,用一个值来表示。 例如 int 类型,常常拿 0 或者-1 作无效值常量。 但有时候,0 或-1 也是有效值时,这种做法就不生效了。或者说 项目组内 有时候拿 0,有时候拿-1 作无效值标记,很不统一。我们借鉴 sql 及 c#,引入 可空值概念,用 null 表达空值。 +- 我们为 Item 添加 min_use_level 字段,类型为 int? 当填有效值时,使用时要检查等级,否则不检查。 +- [定义](images/adv/def_36.png) + ``` xml + + ... + + + ``` +- 配置 + ![如图](images/adv/def_37.png) + ## 列表类型 list,int - 我们新增一个字段, 宝箱的随机抽取道具列表 random_item_ids。 @@ -375,45 +415,22 @@ text的key和text字段都是string类型,因此在连续单元格或者sep产 - 定义不变,但 excel 的填法有区别,数据如下: - ![如图](images/adv/def_35.png) -## 可空变量 -- 有时候会有一种变量,我们希望它 功能生效时填一个有效值,功能不生效里,用一个值来表示。 例如 int 类型,常常拿 0 或者-1 作无效值常量。 但有时候,0 或-1 也是有效值时,这种做法就不生效了。或者说 项目组内 有时候拿 0,有时候拿-1 作无效值标记,很不统一。我们借鉴 sql 及 c#,引入 可空值概念,用 null 表达空值。 -- 我们为 Item 添加 min_use_level 字段,类型为 int? 当填有效值时,使用时要检查等级,否则不检查。 -- [定义](images/adv/def_36.png) - ``` xml - - ... - - - ``` -- 配置 - ![如图](images/adv/def_37.png) +## 默认值 -## datetime 类型 +该特性只对excel格式文件有效。当单元格为空时,该字段使用默认值。 -- 时间是常用的数据类型。Luban 特地提供了支持。 - 有两种形式,一种以纯字符串的方式填写。 - - 以纯字符串方式填写 - 填写格式为 以下 4 种。 - - yyyy-mm-dd hh:mm:ss 如 1999-08-08 01:30:29 - - yyyy-mm-dd hh:mm 如 2000-08-07 07:40 - - yyyy-mm-dd hh 如 2001-09-05 07 - - yyyy-mm-dd 如 2003-04-05 - - 以 excel内置的时间格式填写 - -- 为 Item 新增一个 失效时间字段 expire_time 。 -- [定义](images/adv/def_38.png) - ``` xml - - ... - - - ``` -- 配置 - 图中红框中第一个时间以 excel的时间格式填写 - 剩下第2,3,4个时间以 纯字符串形式填写 +```xml + + + + - ![如图](images/adv/def_39.png) +
+ +``` + +![default](images/adv/def_50.png) ## convert 常量替换 diff --git a/docs/images/adv/def_50.png b/docs/images/adv/def_50.png new file mode 100644 index 0000000000000000000000000000000000000000..e39fc3f6224d942a98805a285b3992e5739d0cdb GIT binary patch literal 7468 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 literal 0 HcmV?d00001 diff --git a/src/Luban.Job.Cfg/Source/Generate/DataScatterRender.cs b/src/Luban.Job.Cfg/Source/Generate/DataScatterRender.cs index 8d0237f..d684450 100644 --- a/src/Luban.Job.Cfg/Source/Generate/DataScatterRender.cs +++ b/src/Luban.Job.Cfg/Source/Generate/DataScatterRender.cs @@ -14,6 +14,9 @@ namespace Luban.Job.Cfg.Generate [Render("data_json")] [Render("data_json2")] [Render("data_lua")] + [Render("data_xml")] + [Render("data_yaml")] + [Render("data_erlang")] class DataScatterRender : DataRenderBase { public override void Render(GenContext ctx) diff --git a/src/Luban.Job.Cfg/Source/RenderExtension.cs b/src/Luban.Job.Cfg/Source/RenderExtension.cs index fb5af5e..d78fe50 100644 --- a/src/Luban.Job.Cfg/Source/RenderExtension.cs +++ b/src/Luban.Job.Cfg/Source/RenderExtension.cs @@ -1,3 +1,4 @@ +using Luban.Job.Cfg.Datas; using Luban.Job.Cfg.Defs; using Scriban; using System.Collections.Generic; @@ -23,5 +24,24 @@ namespace Luban.Job.Cfg ctx.PushGlobal(env); return template.Render(ctx); } + + public static string RenderData(this Template template, DefTable table, List exportDatas, Dictionary extraModels = null) + { + var ctx = new TemplateContext(); + var env = new TTypeTemplateExtends + { + ["table"] = table, + ["datas"] = exportDatas, + }; + if (extraModels != null) + { + foreach ((var k, var v) in extraModels) + { + env[k] = v; + } + } + ctx.PushGlobal(env); + return template.Render(ctx); + } } } diff --git a/src/Luban.Job.Cfg/Source/Utils/DataExporterUtil.cs b/src/Luban.Job.Cfg/Source/Utils/DataExporterUtil.cs index 6dcd02e..e7f5e24 100644 --- a/src/Luban.Job.Cfg/Source/Utils/DataExporterUtil.cs +++ b/src/Luban.Job.Cfg/Source/Utils/DataExporterUtil.cs @@ -5,6 +5,8 @@ using Luban.Job.Cfg.DataVisitors; using Luban.Job.Cfg.Defs; using Luban.Job.Cfg.l10n; using Luban.Job.Cfg.RawDefs; +using Luban.Job.Common.Utils; +using Scriban; using System; using System.Collections.Generic; using System.IO; @@ -19,6 +21,11 @@ namespace Luban.Job.Cfg.Utils { public static object ToOutputData(DefTable table, List records, string dataType) { + if (StringTemplateUtil.TryGetTemplate($"config/data/{dataType[5..]}", out Template template)) + { + return template.RenderData(table, records.Select(r => r.Data).ToList()); + } + switch (dataType) { case "data_bin": diff --git a/src/Luban.Job.Common/Source/Utils/StringTemplateUtil.cs b/src/Luban.Job.Common/Source/Utils/StringTemplateUtil.cs index 6235303..8586e11 100644 --- a/src/Luban.Job.Common/Source/Utils/StringTemplateUtil.cs +++ b/src/Luban.Job.Common/Source/Utils/StringTemplateUtil.cs @@ -18,16 +18,27 @@ namespace Luban.Job.Common.Utils TemplateSearchPaths.Add(path); } - public static string GetTemplateString(string templateName) + public static bool TryGetTemplateString(string templateName, out string result) { foreach (var searchPath in TemplateSearchPaths) { var fullPath = $"{searchPath}/{templateName}.tpl"; if (File.Exists(fullPath)) { - return File.ReadAllText(fullPath, Encoding.UTF8); + result = File.ReadAllText(fullPath, Encoding.UTF8); + return true; } } + result = null; + return false; + } + + public static string GetTemplateString(string templateName) + { + if (TryGetTemplateString(templateName, out var strTpl)) + { + return strTpl; + } throw new FileNotFoundException($"can't find {templateName}.tpl in paths:{string.Join(';', TemplateSearchPaths)}"); } @@ -38,6 +49,21 @@ namespace Luban.Job.Common.Utils return s_templates.GetOrAdd(templateName, tn => Template.Parse(GetTemplateString(tn))); } + public static bool TryGetTemplate(string templateName, out Template template) + { + if (s_templates.TryGetValue(templateName, out template)) + { + return true; + } + if (TryGetTemplateString(templateName, out var strTpm)) + { + template = s_templates.GetOrAdd(templateName, tn => Template.Parse(strTpm)); + return true; + } + template = null; + return false; + } + public static Template GetOrAddTemplate(string templateName, Func creator) { return s_templates.GetOrAdd(templateName, creator); diff --git a/src/Luban.Server/Properties/launchSettings.json b/src/Luban.Server/Properties/launchSettings.json index 13d062e..59ed2f6 100644 --- a/src/Luban.Server/Properties/launchSettings.json +++ b/src/Luban.Server/Properties/launchSettings.json @@ -2,6 +2,10 @@ "profiles": { "Luban.Server": { "commandName": "Project" + }, + "TestTemplate": { + "commandName": "Project", + "commandLineArgs": "-t D:\\workspace\\luban_examples\\Projects\\CustomTemplates" } } } \ No newline at end of file