diff --git a/README.md b/README.md index 56bc5ef..dd578b4 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ ## 介绍 -luban是一个相当完备成熟的游戏配置解决方案,同时也可以用作通用型对象生成与缓存工具。 +luban是一个比较完备的游戏配置解决方案,同时也可以用作通用型对象生成与缓存工具。 luban创新性提出 **定义 + 数据源** 的设计,实现了完备的类型系统,增强了excel格式,同时提供json、xml、lua等多种数据源支持,统一了数据定义、加载、检验、数据导出及代码生成的游戏配置Pipeline,彻底解决了中大型项目中难以在excel中配置复杂数据以及一个项目中excel、json等多种的配置方案并存的问题。 @@ -30,12 +30,13 @@ Luban另一优点是生成过程极快。对于普通的导表工具,一个典 - [Excel 配置数据简介](docs/data_excel.md) - [使用说明](docs/catalog.md) - [常见问题](docs/faq.md) +- [示例项目](https://github.com/focus-creative-games/luban_examples) ## 特性 - 支持增强的excel格式,可以在excel里比较简洁填写出任意复杂的数据。 -- 支持excel族、json、xml、lua 多种数据格式 -- 强大完备的类型系统。支持所有常见原生类型、容器类型list,set,map、枚举和结构、**多态结构**以及**可空类型** -- 灵活的数据源定义。一个表可以来自多个文件,或者一个文件内定义多个表或者一个表对应一个目录下所有文件。以及以上的组合 +- 支持excel族、json、xml、lua 多种数据格式,基本统一了游戏内的配置数据 +- 灵活的数据源定义。一个表可以来自多个文件或者一个文件内定义多个表或者一个表对应一个目录下所有文件,以及以上的组合。 +- 强大完备的类型系统。支持所有常见原生类型、容器类型list,set,map、枚举和结构、**多态结构**以及**可空类型**。 - 支持表与字段级别分组。可以选择性地导出客户端或者服务器所用的表及字段。 - 多种导出数据格式支持。支持binary、json、lua 等导出数据格式。 - 支持数据标签。 可以选择导出符合要求的数据,发布正式数据时策划不必手动注释掉那些测试或者非正式数据了。 @@ -71,9 +72,9 @@ Luban另一优点是生成过程极快。对于普通的导表工具,一个典 - 其他所有支持lua的引擎和平台 - 其他所有支持js的引擎和平台 -## 结构定义与配置表填写 +## 使用预览 -与常见的专注于excel的导表工具中定义和数据放在同一个excel文件的做法不同,luban的定义与数据分离,使用单独的xml定义 **表和结构**,数据文件只包含数据。 +与常见的专注于excel的导表工具不同,luban的定义与数据分离,使用单独的xml定义 **表和结构**,数据文件只包含数据。 ### 常规的原生数据 ``` @@ -414,7 +415,7 @@ luban支持横表与纵表,默认为横表。对于单例表,纵表填写更 ``` -### json 数据源 +### lua 数据源 定义 @@ -496,7 +497,7 @@ luban支持横表与纵表,默认为横表。对于单例表,纵表填写更 ```Lua -- 直接 require 表 - local data = require("<--output_data_dir 指向的lua相对路径>.TbFullTypes") + local data = require(".TbFullTypes") -- 获取 key为1001的道具数据 local cfg = data[1] print(cfg.X4) @@ -506,7 +507,7 @@ luban支持横表与纵表,默认为横表。对于单例表,纵表填写更 ```C# // 一行代码可以加载所有配置。 cfg.Tables 包含所有表的一个实例字段。 - var tables = new cfg.Tables(file => return new ByteBuf(File.ReadAllBytes("<--output_data_dir指向的数据路径>/" + file))); + var tables = new cfg.Tables(file => return new ByteBuf(File.ReadAllBytes("<数据路径>/" + file))); // 访问一个单例表 Console.WriteLine(tables.TbGlobal.Name); // 访问普通的 key-value 表 diff --git a/src/Luban.Client/Source/Utils/FileCleaner.cs b/src/Luban.Client/Source/Utils/FileCleaner.cs index 8051038..d1a2702 100644 --- a/src/Luban.Client/Source/Utils/FileCleaner.cs +++ b/src/Luban.Client/Source/Utils/FileCleaner.cs @@ -1,5 +1,7 @@ +using System; using System.Collections.Generic; using System.IO; +using Luban.Common.Utils; namespace Luban.Client.Common.Utils { @@ -67,7 +69,7 @@ namespace Luban.Client.Common.Utils { s_logger.Trace("remain file:{file}", file); } - else + else if(!_ignoreFileExtensions.Contains(FileUtil.GetFileExtension(file))) { s_logger.Info("[remove] file: {file}", file); File.Delete(file); @@ -76,7 +78,7 @@ namespace Luban.Client.Common.Utils // 清除空目录 var subDirs = new List(Directory.GetDirectories(dir, "*", SearchOption.AllDirectories)); - subDirs.Sort((a, b) => -a.CompareTo(b)); + subDirs.Sort((a, b) => -String.Compare(a, b, StringComparison.Ordinal)); foreach (var subDir in subDirs) { string fullSubDirPath = Path.GetFullPath(subDir); diff --git a/src/Luban.Common/Source/Utils/FileUtil.cs b/src/Luban.Common/Source/Utils/FileUtil.cs index 07b393b..3f3f1dc 100644 --- a/src/Luban.Common/Source/Utils/FileUtil.cs +++ b/src/Luban.Common/Source/Utils/FileUtil.cs @@ -22,6 +22,18 @@ namespace Luban.Common.Utils return index >= 0 ? path[..index] : path; } + public static string GetFileNameWithoutExt(string file) + { + int index = file.LastIndexOf('.'); + return index >= 0 ? file.Substring(0, index) : file; + } + + public static string GetFileExtension(string file) + { + int index = file.LastIndexOf('.'); + return index >= 0 ? file.Substring(index + 1) : ""; + } + public static string GetPathRelateRootFile(string rootFile, string file) { return Combine(GetParent(rootFile), file); diff --git a/src/Luban.Job.Cfg/Source/DataVisitors/LuaExportor.cs b/src/Luban.Job.Cfg/Source/DataVisitors/LuaExportor.cs index d697e70..f138229 100644 --- a/src/Luban.Job.Cfg/Source/DataVisitors/LuaExportor.cs +++ b/src/Luban.Job.Cfg/Source/DataVisitors/LuaExportor.cs @@ -125,9 +125,14 @@ namespace Luban.Job.Cfg.DataVisitors line.Append(type.Value); } + private string EscapeString(string s) + { + return s.Replace("\\", "\\\\").Replace("'", "\\'"); + } + public void Accept(DString type, StringBuilder line) { - line.Append('\'').Append(type.Value).Append('\''); + line.Append('\'').Append(EscapeString(type.Value)).Append('\''); } public void Accept(DBytes type, StringBuilder line) @@ -137,7 +142,7 @@ namespace Luban.Job.Cfg.DataVisitors public void Accept(DText type, StringBuilder line) { - line.Append('\'').Append(type.Value).Append('\''); + line.Append('\'').Append(EscapeString(type.Value)).Append('\''); } public void Accept(DBean type, StringBuilder line) diff --git a/src/Luban.Job.Cfg/Source/Defs/TTypeTemplateExtends.cs b/src/Luban.Job.Cfg/Source/Defs/TTypeTemplateExtends.cs index bbd1351..b713190 100644 --- a/src/Luban.Job.Cfg/Source/Defs/TTypeTemplateExtends.cs +++ b/src/Luban.Job.Cfg/Source/Defs/TTypeTemplateExtends.cs @@ -59,7 +59,7 @@ namespace Luban.Job.Cfg.Defs var table = field.Assembly.GetCfgTable(field.Ref.FirstTable); if (field.IsNullable) { - return $"this.{refVarName} = this.{name} != null ? (({table.FullNameWithTopModule})_tables.get(\"{tableName}\")).get({name}.Value) : null;"; + return $"this.{refVarName} = this.{name} != null ? (({table.FullNameWithTopModule})_tables.get(\"{tableName}\")).get({name}) : null;"; } else { diff --git a/src/Luban.Job.Cfg/Source/Generate/CppBinCodeRender.cs b/src/Luban.Job.Cfg/Source/Generate/CppBinCodeRender.cs index 1bbfba7..97d7a46 100644 --- a/src/Luban.Job.Cfg/Source/Generate/CppBinCodeRender.cs +++ b/src/Luban.Job.Cfg/Source/Generate/CppBinCodeRender.cs @@ -51,6 +51,7 @@ class {{name}} : public {{if parent_def_type}} {{parent_def_type.cpp_full_name}} } +{{~if !hierarchy_export_fields.empty?~}} {{name}}({{- for field in hierarchy_export_fields }}{{cpp_define_type field.ctype}} {{field.name}}{{if !for.last}},{{end}} {{end}}) {{-if parent_def_type-}} : {{parent_def_type.cpp_full_name}}({{ for field in parent_def_type.hierarchy_export_fields }}{{field.name}}{{if !for.last}}, {{end}}{{end}}) @@ -61,7 +62,7 @@ class {{name}} : public {{if parent_def_type}} {{parent_def_type.cpp_full_name}} this->{{field.cpp_style_name}} = {{field.name}}; {{~end~}} } - +{{~end~}} virtual ~{{name}}() {} bool deserialize(ByteBuf& _buf); diff --git a/src/Luban.Job.Cfg/Source/Generate/GoCodeRender.cs b/src/Luban.Job.Cfg/Source/Generate/GoCodeRender.cs index 46587a5..5d6fa5f 100644 --- a/src/Luban.Job.Cfg/Source/Generate/GoCodeRender.cs +++ b/src/Luban.Job.Cfg/Source/Generate/GoCodeRender.cs @@ -136,6 +136,7 @@ func NewChild{{go_full_name}}(_buf *serialization.ByteBuf) (_v interface{}, err private static Template t_tableRender; public string Render(DefTable p) { + // TODO Ŀǰֶֻ֧̬ͨ. ˫key֧ string package = "cfg"; var template = t_tableRender ??= Template.Parse(@" {{- @@ -233,7 +234,16 @@ func New{{go_full_name}}(_buf *serialization.ByteBuf) (*{{go_full_name}}, error) return nil, err2 } else { _dataList = append(_dataList, _v) +{{~if value_type.is_dynamic ~}} + {{- for child in value_type.bean.hierarchy_not_abstract_children}} + if __v, __is := _v.(*{{child.go_full_name}}) ; __is { + dataMap[__v.{{index_field.cs_style_name}}] = _v + continue + } + {{-end}} +{{~else~}} dataMap[_v.{{index_field.cs_style_name}}] = _v +{{~end~}} } } return &{{go_full_name}}{_dataList:_dataList, _dataMap:dataMap}, nil diff --git a/src/Luban.Job.Cfg/Source/Generate/JavaBinCodeRender.cs b/src/Luban.Job.Cfg/Source/Generate/JavaBinCodeRender.cs index fb9da94..b2dc26a 100644 --- a/src/Luban.Job.Cfg/Source/Generate/JavaBinCodeRender.cs +++ b/src/Luban.Job.Cfg/Source/Generate/JavaBinCodeRender.cs @@ -192,7 +192,8 @@ public final class {{name}} public java.util.ArrayList<{{java_box_define_type value_type}}> getDataList() { return _dataList; } {{if value_type.is_dynamic}} - public T getAs({{java_define_type key_type1}} key1, {{java_define_type key_type2}} key2) { return (T)_dataMapMap.get(key1).get(key2); } + @SuppressWarnings(""unchecked"") + public T getAs({{java_define_type key_type1}} key1, {{java_define_type key_type2}} key2) { return (T)_dataMapMap.get(key1).get(key2); } {{end}} public {{java_box_define_type value_type}} get({{java_define_type key_type1}} key1, {{java_define_type key_type2}} key2) { return _dataMapMap.get(key1).get(key2);} @@ -225,7 +226,8 @@ public final class {{name}} public java.util.ArrayList<{{java_box_define_type value_type}}> getDataList() { return _dataList; } {{~if value_type.is_dynamic~}} - public T getAs({{java_box_define_type key_type}} key) { return (T)_dataMap.get(key); } + @SuppressWarnings(""unchecked"") + public T getAs({{java_define_type key_type}} key) { return (T)_dataMap.get(key); } {{~end~}} public {{java_box_define_type value_type}} get({{java_define_type key_type}} key) { return _dataMap.get(key); } diff --git a/src/Luban.Job.Cfg/Source/Generate/Python27JsonCodeRender.cs b/src/Luban.Job.Cfg/Source/Generate/Python27JsonCodeRender.cs index b5c4bab..cd1bde2 100644 --- a/src/Luban.Job.Cfg/Source/Generate/Python27JsonCodeRender.cs +++ b/src/Luban.Job.Cfg/Source/Generate/Python27JsonCodeRender.cs @@ -102,6 +102,9 @@ class {{name}} {{if parent_def_type}}({{parent_def_type.py_full_name}}){{end}}: {{~end~}} {{py27_deserialize ('self.' + field.py_style_name) ('_json_[""' + field.name + '""]') field.ctype}} {{~end~}} + {{~if export_fields.empty?}} + pass + {{~end~}} "); var result = template.RenderCode(b); diff --git a/src/Luban.Job.Cfg/Source/Generate/Python3JsonCodeRender.cs b/src/Luban.Job.Cfg/Source/Generate/Python3JsonCodeRender.cs index f761961..4acc180 100644 --- a/src/Luban.Job.Cfg/Source/Generate/Python3JsonCodeRender.cs +++ b/src/Luban.Job.Cfg/Source/Generate/Python3JsonCodeRender.cs @@ -97,11 +97,14 @@ class {{name}} {{if parent_def_type}}({{parent_def_type.py_full_name}}){{else if {{parent_def_type.py_full_name}}.__init__(self, _json_) {{~end~}} {{~ for field in export_fields ~}} - {{~if !field.ctype.is_nullable~}} + {{~if !field.ctype.is_nullable~}} if _json_['{{field.name}}'] == None: raise Exception() - {{~end~}} + {{~end~}} {{py3_deserialize ('self.' + field.py_style_name) ('_json_[""' + field.name + '""]') field.ctype}} {{~end~}} + {{~if export_fields.empty?}} + pass + {{~end~}} "); var result = template.RenderCode(b); diff --git a/src/Luban.Job.Cfg/Source/JobController.cs b/src/Luban.Job.Cfg/Source/JobController.cs index 778d6f3..36f1348 100644 --- a/src/Luban.Job.Cfg/Source/JobController.cs +++ b/src/Luban.Job.Cfg/Source/JobController.cs @@ -529,20 +529,20 @@ export class Vector2 { { @" class Vector2: - def __init__(self, x, y): - self.x = x - self.y = y - self.a = Vector4(1,2,3,4) - def __str__(self): - return '{%g,%g}' % (self.x, self.y) + def __init__(self, x, y): + self.x = x + self.y = y + self.a = Vector4(1,2,3,4) + def __str__(self): + return '{%g,%g}' % (self.x, self.y) - @staticmethod - def fromJson(_json_): - x = _json_['x'] - y = _json_['y'] - if (x == None or y == None): - raise Exception() - return Vector2(x, y) + @staticmethod + def fromJson(_json_): + x = _json_['x'] + y = _json_['y'] + if (x == None or y == None): + raise Exception() + return Vector2(x, y) class Vector3: