Compare commits

..

227 Commits

Author SHA1 Message Date
Carson - 宝鱼 720e2eaf49
[fix] 解决7c0579b4bbd7d2c746a8a264d54557ec1e209fd4提交造成的编译错误 (#55)
Co-authored-by: 宝鱼 <kteong1012@outlook.com>
2023-07-06 08:28:08 +08:00
SaNeOr 7c0579b4bb
[fix] 修复 导出python数据, DBool类型 格式问题 (#53) 2023-06-25 18:02:55 +08:00
宝鱼 b791b4276d [new] 新增正则校验器 2023-06-23 12:28:47 +08:00
walon de075ca7d9 [change] DataUtil由GBK编码改为UTF8 2023-06-23 12:21:24 +08:00
SaNeOr 803145e267
[fix] 修复 导出python数据, DMap类型 key的格式问题 (#49)
python的dict类型key不像json只能为string,还可能为其他类型。
2023-06-15 16:46:27 +08:00
walon ea1dc4a462 [fix] 修复data_json_monolithic生成失败的问题 2023-05-12 12:56:33 +08:00
walon 62b60ac581 更新README中文档链接到luban-doc 2023-05-11 17:34:51 +08:00
walon 9bc74b3c73 [fix] 修复ref指向singleton表时,校验错误地使用了ref定义所在的字段而不是ref指向的表的字段的bug 2023-05-06 13:04:44 +08:00
walon 4ef2d70398 [fix] 修复config cpp对多联合索引生成的代码的编译错误 2023-04-12 00:36:41 +08:00
walon 8e68ab0fcd [fix] 修复cfg ts加载bin格式datetime类型时返回bigint的bug 2023-04-03 19:19:42 +08:00
walon bcf81ffe26 [fix] 修复 Proto及DB生成时,对于多级继承在某些遍历顺序下 CollectHierarchyFields 的bug 2023-02-28 21:31:16 +08:00
walon c14437aa59 [fix] 修复Cfg DefBean.PreCompile中CollectHierarchyFields时由于顺序原因有可能发生祖父及更高层级的类的ParentDefType字段未设置导致的未能收集到所有字段的恶劣bug 2023-02-28 20:36:43 +08:00
walon 16e4fe2452 [fix] 修复使用Scriban导出大型文本型数据时发生超出最大迭代次数的问题 2023-02-10 14:37:07 +08:00
walon 7ad942b7cd [fix] 修复生成 java array类型代码的编译bug 2023-02-06 21:33:46 +08:00
walon e8ac256e92 [fix] 修复生成map类型的java resolve代码的有编译错误的问题 2023-02-06 21:12:24 +08:00
walon 46cc7cd26f [change] 删除 ParseExcelBool中对 '是'和'否'的支持 2023-02-03 22:47:45 +08:00
walon e67082a098 [fix] 修复生成db cs代码的编译错误
[change] 生成db cs代码由异步模式换成同步模式
2023-02-03 22:47:45 +08:00
wmltogether ae87bf3f52
[change] 使用int64存储datetime类型,避免读取日期超过2038年出现问题 (#38) 2023-01-29 13:42:46 +08:00
walon ea30f8c202 [fix] 修复db生成代码的bug 2023-01-06 21:49:38 +08:00
Skyhand 874f966a40
[opt] verify param template_search_path (#37) 2022-12-30 18:12:48 +08:00
Carson - 宝鱼 b37f2692e3
[fix] 修复生成java嵌套容器代码有编译错误的bug 2022-12-28 09:50:00 +08:00
Carson - 宝鱼 ba2c5c56e7
[new] 增加java对容器ref的支持 (#34)
[new] 增加java对容器ref的支持
2022-12-14 09:56:04 +08:00
walon d0587324f5 [opt] 优化excel中填写bool类型数据,可以用 true|yes|y|1表示true,用 false|no|n|0表示false 2022-12-07 19:28:46 +08:00
walon 2962b6a1bf
Merge pull request #32 from fgc0109/fix_publish
fix publish single file
2022-12-07 10:58:56 +08:00
Chasel 44b0fc9b36 fix System.Reflection.Assembly.GetCallingAssembly().Location return empty string when publish single file 2022-12-06 17:35:31 +08:00
walon 33b7f25fd4 [fix] 修复excel格式中仅非有效字段列非空的行被判定为非空行,导致解析数据失败的bug 2022-12-05 18:04:24 +08:00
walon 9b97f5d0bd [new] 支持proto java代码生成 2022-11-28 20:17:12 +08:00
宝鱼 88ab94e836 fix: 修复上个commit引发的空引用报错 2022-11-22 18:19:13 +08:00
Carson - 宝鱼 410fdd3618
[fix] 修复了$type-$value的形式没有读取bean在表格添加的sep符号的bug。 (#30)
Co-authored-by: 宝鱼 <kteong1012@outlook.com>
2022-11-18 12:26:13 +08:00
Carson - 宝鱼 bcc7bc8b37
[new]支持$type + 流式$value的填写方式 (#29)
[new]支持$type + 流式$value的填写方式

Co-authored-by: Tianbao Lin <kteong1012@outlook.com>
2022-10-20 09:04:42 +08:00
walon 165ad69a5d
Merge pull request #27 from kteong1012/pr221019
[fix] 修复了beans定义表不能指定单独sheet的bug
2022-10-19 10:39:51 +08:00
Tianbao Lin b1c2507d99 [fix] 修复了beans定义表不能指定单独sheet的bug 2022-10-19 10:33:02 +08:00
walon af492cea8d [fix] 修复 cs_unity_editor_json/bean.tpl 多了一个括号的bug 2022-10-18 22:39:25 +08:00
Carson - 宝鱼 30a4ce12db
[new] 新增表格对##group的支持
[new] 新增表格对##group的支持


Co-authored-by: Tianbao Lin <kteong1012@outlook.com>
2022-10-13 17:48:42 +08:00
walon 14faa26565
Merge pull request #23 from kteong1012/main
[fix] 修复了导出多维数组失败的bug
[fix] 修复CS模板处理嵌套命名空间的bug
2022-09-23 10:05:32 +08:00
Carson Lin 472c912f0f fix: 修复了导出多维数组失败的问题。 2022-09-22 15:22:38 +08:00
Carson Lin e897d6b5c0 fix: 修改CS模板,修复严重bug 2022-09-22 15:18:23 +08:00
walon 70b5a9b061
Merge pull request #21 from kteong1012/pr220910
[new] config c#代码生成支持无命名空间
2022-09-19 17:01:32 +08:00
Carson Lin 15a5a627d5 opt:修改一些细节 2022-09-16 10:55:56 +08:00
walon c707935008 [feature] flags类型的enum支持以枚举项为子列的配置方式 2022-09-14 12:48:20 +08:00
walon 81295cbfe1 [fix] 修复对容器类型的element type为external type时,生成的cs代码错误对external type调用?.Resove的bug 2022-09-14 12:20:20 +08:00
Carson Lin d9013167b8 feat : 支持无命名空间 2022-09-10 11:16:00 +08:00
walon ad4acf337f [opt] 优化生成的emmylua注解中的类型信息 2022-09-07 17:53:54 +08:00
walon f25f91b94d [fix] 修复code_cs_dotnet_json生成的external type代码有编译错误的bug 2022-08-23 10:11:25 +08:00
walon 741d820587
Merge pull request #19 from kteong1012/pr220816
feat:支持tables.xlsx中value_type定义parent,定义格式为TypeChild#parent=TypeParent
2022-08-22 08:42:24 +08:00
Carson Lin c1b4070520 fix:添加一个判空 2022-08-16 19:32:11 +08:00
Carson Lin 4e2891e07b feat:支持tables.xlsx中value_type定义parent,定义格式为TypeChild#parent=TypeParent 2022-08-16 19:01:30 +08:00
walon b1f9105773 [new] excel __beans__.xlsx 支持 alias字段 2022-08-14 16:38:20 +08:00
walon 69a0acf63e [fix] 修复为cs_unity_json生成 反序列化external bean类型的编译错误bug 2022-08-10 20:22:04 +08:00
walon ff6e09ec9d [fix] 修复xml定义table时失误将options属性写成parser_mode的bug 2022-08-10 18:03:10 +08:00
walon c60f91fdd4 [new] 支持纯流式模式的excel解析方式 2022-08-10 15:22:51 +08:00
walon 154d55f372 [fix] 修复为了支持嵌套容器导致未检查容器元素类型不能为可空的bug 2022-08-09 11:51:21 +08:00
walon 2dd826bafb [change] 修复一些 README.md 错误 2022-08-05 00:44:13 +08:00
walon 412a400471 [fix] 修复ref引用了被过滤的记录却未报告引用错误的bug 2022-07-28 17:02:44 +08:00
walon c738b4dcad
Merge pull request #18 from kteong1012/main
[new] 添加cpp的联合多主键解析
2022-07-26 22:44:10 +08:00
Carson Lin a39339bd51 feat:添加cpp的联合多主键解析
Signed-off-by: Carson Lin <396098651@qq.com>
2022-07-26 21:02:26 +08:00
walon 699b5a167a [new] range和size都支持固定大小或者区间段 xxx=value 或 xxx=[a,b] 的写法 2022-07-19 12:58:02 +08:00
walon 665b9a4cea [new] 新增bidx,即binary格式的索引文件输出。方便优化按需加载 2022-07-19 11:42:49 +08:00
walon abcacebcaa
Merge pull request #14 from kteong1012/main
[new] 支持嵌套容器,支持多维数组和list,array,map的相互嵌套
2022-07-18 19:16:58 +08:00
walon c0706a84f1 [new] 新增Bson导出格式支持 2022-07-18 18:03:53 +08:00
Carson Lin bfc13bc35f fix: 修复TrimBracePairs方法可能会出现的问题 2022-07-13 12:50:13 +08:00
Carson Lin 02f3ebd979 feat: 弃用Type的CollectionLevel,改为在visitor添加depth参数以实现嵌套容器的解析 2022-07-13 11:41:32 +08:00
Carson Lin 79940dcf09 feat: 在TType添加CollectionLevel,用于连续嵌套容器时生成解析代码 2022-07-13 10:28:09 +08:00
carson 6620cca6e1 feat:支持多维数组嵌套 2022-07-13 03:47:23 +08:00
Carson Lin d6ad1e64c4 fea:支持嵌套容器,暂时只支持list 2022-07-12 22:30:14 +08:00
walon c0ddce497c [fix] config enum的java代码生成普通的int,而不是java枚举类。修复flags=1的枚举项反序列化的异常。 2022-06-25 18:29:59 +08:00
walon 4255ae25bf [update] 更新文档,补充godot相关支持的描述,补充c++语言使用示例 2022-06-15 21:01:53 +08:00
Dongua ba1a552d3e [change]gdscript 支持多独立主键 2022-06-11 16:40:15 +08:00
Dongua 60faab741e 【特性】支持gdscript语言 2022-06-09 12:37:45 +08:00
walon f756ab2aa2 [fix] 修复当有两个input文件内容完全相同的情况下,TableDataInfo::BuildIndexs 抛出key重复异常的bug 2022-06-01 17:02:29 +08:00
Dongua 9b086767a1 [change]修改reademe 格式问题,修改链接指向新地址 2022-05-31 15:25:14 +08:00
walon c39b6a623d [new] 添加友情链接 2022-05-29 19:18:39 +08:00
walon 827eca027c [change] 将GenClient与GenServer的最大协议大小由20M改为100M,解决单个表数据文件过大时,无法传输文件的问题 2022-05-07 18:34:01 +08:00
walon e3dc909514 [fix] 修复无法读取纵表多级标题头的bug 2022-05-05 10:44:10 +08:00
walon 6d991ef000 [fix] 修复table设置了mode=map,但index为空时,抛出key不能为多个错误的bug 2022-04-16 15:52:28 +08:00
walon 3f2b2c71eb [new] 新增xml导出数据格式 2022-04-15 17:30:10 +08:00
walon 861f40c29b [new] 支持启用宏的excel格式,即xlsm格式。 2022-04-11 16:11:31 +08:00
walon d9a2e0f83b [opt] 优化错误为int之类的字段使用子列名时的报错信息 2022-04-10 13:33:06 +08:00
walon f7814a0612 [opt] 优化配置中错误创建了抽象类型时的错误提示 2022-04-06 17:14:15 +08:00
walon 142e3ddf93 【修复】修复当存在namespace大小写不同的类时,会出现反复新增并且删除生成的代码文件的问题 2022-03-30 17:46:26 +08:00
walon 7b7ab7b8ba 【优化】检查table.name和value_type,不允许为空白字符串 2022-03-29 00:44:06 +08:00
walon df932046e4 【优化】导出的json格式对于 '\','<' 之类的字段不escape 2022-03-28 18:54:37 +08:00
walon 7bc489e15e 【优化】path检查时严格检查文件大小写 2022-03-27 16:44:06 +08:00
walon 82fbdb513f 【优化】 对于group属性失误用于type添加错误提示
【优化】对于sep失误写成seq添加错误提示
2022-03-27 15:20:20 +08:00
walon c146cf6031
Merge pull request #10 from kteong1012/main
pull request
2022-03-26 17:15:24 +08:00
Carson ee6bba3414 【修复】修复了table和enum的excel表在子sheet的时候通过@定位会报错的问题。 2022-03-26 15:36:37 +08:00
Carson 9886bd1222 【新增】让Map类型支持纵向填充。 2022-03-26 03:37:48 +08:00
walon 71185c4f3d 【修复】修复多态bean定义时子bean在var之后的错误提示 2022-03-15 12:34:07 +08:00
walon ced29f9fca 【修复】修复生成的go代码读取可空变量的bug 2022-03-14 11:10:48 +08:00
walon dee9bcdfe0 【修复】修复output_data_dir目录名末尾包含'/'或'\'时,FileCleaner判定文件存在出错,导致反复add和remove的bug 2022-03-13 11:06:41 +08:00
walon 4cc52c25a3 【修复】修复bool格式的导出错误 2022-03-12 18:09:02 +08:00
walon 69b54fcd91 【特性】新增 data_yaml 导出格式支持 2022-03-12 17:00:54 +08:00
walon fc0772d6f6 【修复】修复从数据excel表头读取多态表结构定义时,误将$type当作普通字段的bug 2022-03-03 14:01:08 +08:00
walon 68c48d0c02 【修复】修复 code_cs_unity_editor_json 解析多态数据代码,如果namespace为空,生成重复case 项的bug 2022-03-02 18:03:37 +08:00
walon 856f30df5b 【修复】修复code_cs_unity_editor_json无法加载多态数据的问题 2022-03-02 16:51:07 +08:00
walon 0e3782dd48 【调整】config gen_gype 'code_cs_json' 改名为 'code_cs_dotnet_json',与'code_cs_unity_json' 区分,避免误用 2022-02-26 11:04:16 +08:00
walon aab29ed828 【修复】修改导出lua数据未正确处理换行符之类的escape的bug 2022-02-24 19:08:35 +08:00
walon c0d91dfa81 【调整】config code_cs_unity_editor_json生成的enum类改成普通枚举类。额外生成一个枚举元数据类。 2022-02-24 11:10:10 +08:00
walon e977a363ef 【调整】json、xml、lua、yaml等数据源支持int类型的enum数据 2022-02-24 10:33:09 +08:00
walon 480b839d31 【文档】补充一个复杂的多级多行表的例子 2022-02-23 15:11:25 +08:00
walon 2335a82c11 【优化】不允许两个自定义类型忽略大小写后同名,避免生成代码文件的平台兼容性问题(例如win下文件名大小写不敏感,如果允许,则会生成到同一个文件名,引起编译问题)
【优化】关闭Luban.Client生成结束后打印的Socket错误日志。
2022-02-23 13:03:58 +08:00
walon 7901b7012b 【优化】Luban.Client写入生成的文件时使用自定义的WriteFileAllBytes,即使文件被占用情况下也能写入。 2022-02-23 12:37:00 +08:00
walon c79e6169a6 【修复】修复path校验子资源路径匹配判定的问题 2022-02-19 19:29:50 +08:00
walon 13ee28835a 【修复】修复读取vector2,3,4类型数据时,未检查数据过多的问题,例如 vector2类型填了'1,2,4' 2022-02-16 20:43:19 +08:00
walon b2014ab7bb 【新增】vector{2,3,4}和datetime类型也支持 externaltype 映射 2022-02-16 18:03:57 +08:00
walon d5184e136e 【新增】config新增gen_type类型 code_cs_unity_bin,生成代码与code_cs_bin基本相同,除了使用 UnityEngine.Vector外 2022-02-16 17:24:26 +08:00
walon 3631f131ad 【优化】对于flags类型enum,填整数值时不再要求是枚举值中某一个 2022-02-16 09:58:52 +08:00
walon 034f0dd21b 【修复】修复proto c#对可空变量序列化的bug 2022-02-13 11:57:55 +08:00
walon c52407bdee 【调整】excel之类的格式读取多态类型时,优先查找$type之类字段,再查找默认__type__字段
【修复】修复生成protocol序列化可空类型的代码的编译错误
2022-02-12 21:46:09 +08:00
walon ba269df862 【修复】修复lua_bin生成的代码使用_name作为多态key的bug。应该_type_。 2022-02-12 13:42:12 +08:00
walon 657bd19c77 【调整】【重大】将配置中的多态类型字段由统一的__type__改成 excel优先$type,json优先$type,xml优先type,lua优先_type_,如果找不到再找默认__type__。 2022-02-12 13:23:26 +08:00
walon e76b20b24f 【重构】重构 cpp_bin 生成,全部可以在模板中定制
【重构】重构 cpp_ue_editor_json、cpp_ue_bp、cs_unity_editor_json 生成
2022-02-12 12:33:08 +08:00
walon caa476272b 【更新】根据最新README.md更新了README.en-us.md 2022-02-12 10:44:18 +08:00
walon 953469475c 【修复】修复无法识别纵表标签 ##column#var 的bug 2022-02-11 18:09:24 +08:00
walon e8805873ea 【新增】config 容器元素为ref时,也为生成相应的ref变量并且resolve。 目前只支持c# 2022-02-11 11:48:23 +08:00
walon e6203df5b3 【优化】将tpl文件中__type__改为 {{x.json_type_name_key}},方便统一调整。 2022-02-10 18:06:29 +08:00
walon 6b1669e050 【调整】调整externaltype实现 2022-02-10 15:07:09 +08:00
walon 45f4a13dd2 【修复】修复当意外使用bean或容器等不能作为index类型的字段为table的index时,打印的错误日志没有报告正确的错误的信息的问题。在DefTable Compile时检查并且给出清晰的错误信息。 2022-02-10 12:47:15 +08:00
walon 0b63b1fcb3 【优化】调整&与#的分割语法。除了定义字段的非type以外的属性用&,其他地方都用#。 2022-02-10 12:00:42 +08:00
walon 66e07830d0 【调整】移除对简单容器类型如"list,int"之类自动sep的支持,简化规则 2022-02-10 11:26:45 +08:00
walon 0122d97071
Merge pull request #7 from youngself/develop
【新增】生成EmmeLua文档类型接口
2022-02-03 22:28:22 +08:00
yxy 7d05f1fd4d 【优化】EmmyLuaType的TBytes 2022-02-03 22:18:48 +08:00
yxy a4df2de837 【修复】EmmeLua改为EmmyLua 2022-02-03 22:15:15 +08:00
yxy f3949f9c72 【新增】EmmeLua文档注解接口 2022-02-03 13:43:40 +08:00
walon 1a1fc3ca2f 【调整】删去excel中定义多态结构时,要求parent必须相同命名空间的限制 2022-01-29 15:02:00 +08:00
walon 54e1955112 【修复】修复上次调整sep机制引起的读取简单数据列表只读到第一个字段的问题 2022-01-29 14:54:15 +08:00
walon b3457da8d2 【特性】支持子bean在其他地方,包括其他模块从父类继承 2022-01-29 14:28:03 +08:00
walon 9f687adcf1 【修复】新增code_cs_unity_editor_json时失误改了cs_json的模板。回滚 2022-01-29 11:05:49 +08:00
walon 7f9c4e93d6 【新增】excel格式支持定义多态类型 2022-01-28 17:58:52 +08:00
walon 29cf3b56f6 【修复】修复code_cs_unity_editor_json保存map类型数据的bug 2022-01-26 22:48:17 +08:00
walon f617d5f10f 【特性】 cfg root.xml 新增配置项 option
【特性】新增 code_cs_unity_editor_json 支持
2022-01-26 22:19:40 +08:00
walon 1acaac5b87 【优化】如果枚举类包含value为0的枚举项,允许excel中列限定模式下为空取默认值 2022-01-26 11:25:46 +08:00
walon c20505aea3 【更新】调整 README.md 中的示例 2022-01-25 16:02:22 +08:00
walon f9abe09a8d 【优化】生成的cfg c#代码都为partial类。并且新增PostInit和PostResolve两个partital函数 2022-01-25 15:02:18 +08:00
walon 582edb6cc6 【特性】对c#语言,为联合索引和多重索引的table生成索引代码 2022-01-25 13:23:21 +08:00
walon a41fd92b43 【修复】修复code_cs_bin在table.mode='list'时,加载数据列表为空的bug 2022-01-24 22:24:50 +08:00
walon 1e06690def 【修复】修复table.mode为空时,由于只使用","去拆分index,导致误将key1+key2+key3当作一个key,而猜测mode="map"导致无法找到key的bug 2022-01-23 21:34:31 +08:00
walon c612423590 【更新】更新文档 2022-01-21 12:01:56 +08:00
walon d40c016afa 【修复】修复读取深层次容器未使用sep的bug 2022-01-19 21:16:49 +08:00
walon 36df70bffb 【优化】当excel字段行包含非法tag如group时,打印错误 2022-01-19 18:12:12 +08:00
walon 8cd9e5f247 【修复】修复refgroup可默认性的计算bug 2022-01-06 16:32:35 +08:00
walon a560c23c67 【优化】path检查支持 Assets/xxxx/yyyy[abc] 这样的子资源文件 2022-01-06 10:49:52 +08:00
walon c009d54d26 【修复】修复ref只包含一个refgroup,同时refgroup只包含一个表时,错误判定为需要genRef,导致生成代码出错的bug 2022-01-03 12:51:46 +08:00
walon fc1cb23974 【更新】更新文档 2022-01-01 23:49:05 +08:00
walon 4bd826a360 【调整】调整data_resources输出中tag和资源值的顺序,tag在前,资源值在后 2021-12-27 10:10:46 +08:00
walon 00a56c0a37 【修复】修复data_resources导出 2021-12-24 13:57:31 +08:00
walon 8164d641e4 【调整】生成单个代码文件时,相对目录为output_code_dir而不是当前目录 2021-12-23 17:19:52 +08:00
walon 51dbe96917 【特性】新增cfg python27 json支持 2021-12-22 18:10:35 +08:00
walon 8e4ef0952d 【修复】修复excel中未定义多态或可空类型的__type__列时抛出无法准确定位错误信息的异常的问题 2021-12-22 15:04:28 +08:00
walon b27366932d 更新 README.md 2021-12-22 13:37:44 +08:00
walon 73da39e7cd 【更新】更新文档,替换wiki链接地址为lubandoc。更新英文文档。 2021-12-22 10:06:50 +08:00
walon 7a80812c17 【优化】调整了excel格式,允许使用##var来标识顶层字段,并且允许自由调整顶层字段行的位置 2021-12-20 18:15:14 +08:00
walon 2021e38485 【特性】新增convert_template支持 2021-12-18 20:11:59 +08:00
walon c2fc86215c 【特性】新增 --output:tables, --output:include_tables, --output:exclude_tables 用于指定包含或者排除某些表 2021-12-18 18:50:03 +08:00
walon 6bc3552a1f 【特性】新增unity ScriptableObject asset数据源支持
【优化】允许为目录数据源指定参数,这些参数会作用到所有目录下的子文件上
2021-12-18 13:36:21 +08:00
walon 9e6463f18d 【优化】新增 --go:bright_module_name 参数,优化go的生成代码,使用比较地道的module机制 2021-12-17 10:24:58 +08:00
walon 4f8fd20780 【优化】读取excel标题头列时,如果遇到不认识的标签,打印警告。将示例中的##+全部统一成##var。 2021-12-12 13:12:01 +08:00
walon 8cd9ffee3c 【修复】修复文档错误 2021-12-11 15:39:17 +08:00
walon fb000e400c 【特性】支持 [xxx, xxx] 这种格式来标识一个占据多列的字段,方便csv这种不支持多列合并的格式表达多列字段 2021-12-10 16:32:57 +08:00
walon e650ff6fe6 【特性】新增code_protobuf3及data_protobuf3_json支持
【修复】修复FlatBuffers json导出格式中map数据的格式错误。本应该是{"K":v}却序列化成[[k1,v1]]
2021-12-08 20:39:58 +08:00
walon aa0d0a9a36 【修复】修复LubanAssistant的编译错误。这是LubanAssistant最后一个版本。后面转为Office Add-In开发。 2021-12-07 17:33:56 +08:00
walon c1ffaa2d98 【特性】新增flatbuffers支持,可以生成schema定义,但目前只支持json导出格式(有个问题:不支持union与容器的组合),因为binary格式过于复杂了。 2021-12-06 18:07:18 +08:00
walon 5861001683 【特性】新增msgpack导出支持 2021-12-06 10:36:38 +08:00
walon a78c9abd1a 【特性】新增refgroup,方便很多字段都引用到同一组引用表的情况。 2021-12-05 20:49:00 +08:00
walon 1377eb828d 【修复】修复ref了不属于当前导出分组的table时,生成报错的bug。
【修复】修复未进行完整生成,只生成导出分组表,导致一些ref检查出错的bug。
2021-12-05 17:52:42 +08:00
walon 19656437cb 【优化】灵活并且统一了sep的用法 2021-12-05 16:57:55 +08:00
walon 83f81ed463 【调整】名称调整,统一 pb => protobuf
【特性】新增 gen_types 类型 code_template,可以自定义新的模板目录了,新增配合的命令行选项 --template:code:dir
【调整】模板参数名调整。 data_template_name => template:data:file, output_compact_json => output:data:compact_json
【修复】修复protobuf生成错误设置 language为lua的bug
2021-12-04 14:47:47 +08:00
walon 93595c7748 【优化】如果模板代码生成的内容为空,则不生成此文件 2021-12-04 13:13:53 +08:00
walon 79d52c8820 【修复】从service.manager中获得Tables管理类的类名,而不是写死为Tables。 2021-12-03 18:10:09 +08:00
walon a6a1d58159 【更新】修复文档错误 2021-12-03 17:40:12 +08:00
walon f740685830 【更新】更新文档 2021-12-03 15:30:54 +08:00
walon 8b0f607f4a 【修复】修复一些将field拼成filed的错误
【优化】当table从xlsx中读取定义时,如果value不包含命名空间,则使用table所在的命名空间;如果包含,则使用它的命名空间
【修复】修复从xlsx读取的table定义,未检查table名唯一的bug。
2021-12-02 18:37:26 +08:00
walon 6bcc8e74ce 【修复】修复unity项目中彻底移除某个模块后,重新生成代码过程中会尝试删除该模块目录,由于保留了meta文件导致目录删除失败的bug 2021-12-02 18:11:39 +08:00
walon 91bb1b317f 【修复】修复excel中多层标题头读取可空bean时抛异常的bug 2021-12-02 17:11:49 +08:00
walon edc09b6742 【特性】新增对外部class类的支持。 可以在定义中引用现成的外部类,如UnityEngine.Color 2021-12-02 14:59:25 +08:00
walon 7fc75871d4 【特性】支持 enum 的external type映射。可以将定义中的某个枚举类映射到现成的另一个枚举类。暂时只支持cs语言。 2021-12-02 13:43:33 +08:00
walon ff93a074c4 【调整】调整xlsx中定义enum的格式。完整对应xml定义。 2021-12-02 10:52:06 +08:00
walon 85937685b6 【特性】新增cfg 所有语言对table mode=list的代码生成 2021-12-01 14:12:11 +08:00
walon ba4bb014a9 【特性】新增list表,支持多key联合索引与多key独立索引 2021-12-01 12:33:31 +08:00
walon 967c45dda1 【调整】调整 naming_convetion 相关的一些命令行选项名 2021-11-30 20:44:46 +08:00
walon 46ccf6868b 【修复】修复在某些机器上无法找到"Asia/Shanghai",导致启动失败的问题。新增失败后尝试"China Standard Time"
【修复】修复Luban.ClientServer未初始化DefaultTimeZone,导致默认时区为Utc的bug,严重!
2021-11-30 20:14:57 +08:00
walon 7df2f6ea86 【特性】cfg excel格式支持列表水平展时的多级标题头,可以为每个元素指定列。 2021-11-30 18:50:08 +08:00
walon 57867bc14b 【修复】修复LubanAssistant的编译错误 2021-11-30 18:07:16 +08:00
walon ca8e5035ba 【重构】模块相关函数由StringTemplateUtil移到StringTemplateManager
【特性】Luban.Server支持禁用生成缓存(但仍保留源文件缓存)。
【修复】修复 start_up.md 文档中的链接错误
2021-11-30 16:32:31 +08:00
walon d686f82ba8 【更新】更新文档 2021-11-30 15:08:15 +08:00
walon d571638970 【优化】cfg cs代码的datetime类型额外生成 xxx_Millis字段,返回毫秒值。
【更新】更新文档,补充protobuf,msgpack,flatbuffers相关描述
2021-11-30 12:16:38 +08:00
walon 56c222976e 【修复】修复cfg data_protobuf导出多态结构数据的问题。已经通过所有测试。完美! 2021-11-30 10:32:47 +08:00
walon 6e83d8c1eb 【更新】更新protobuf相关说明 2021-11-30 02:10:14 +08:00
walon 66ba09e8a0 【特性】支持生成 proto定义文件和proto二进制数据 2021-11-30 01:52:47 +08:00
walon b8e794d528 【完善】proto go补充了rpc和stub的实现 2021-11-29 17:57:32 +08:00
walon 813e129352 【调整】调整 proto go和 cfg go的生成 2021-11-29 16:24:02 +08:00
walon a9de6b924b 【特性】新增 proto go支持 2021-11-29 14:31:13 +08:00
walon 1de0299b17 【调整】调整本土化相关选项名,统一为 --{l10n}:{option} 2021-11-29 10:17:48 +08:00
walon e9244a30da 【调整】调整一些语言相关的生成选项名,标准化为 --{language}:{option} 2021-11-29 09:57:12 +08:00
walon bb534ef12e 【重构】重构cfg code生成,消除大量重复代码 2021-11-27 15:23:42 +08:00
walon 28626d0cfd 【特性】新增 proto go实现,有待完善。 2021-11-27 13:45:04 +08:00
walon b0d64bb967 【优化】优化 cfg go的生成代码 2021-11-27 13:36:38 +08:00
walon 7ff3467d35 【修复】修复 proto typescript对 TFlong及Tlong bigint情况下的代码及 array类型的代码的编译问题 2021-11-27 13:27:33 +08:00
walon edd82f249c 【重构】重构proto Render代码,减少不必要的代码复制
【调整】解决proto代码有一些Unity下编译器不支持的语法特性
2021-11-27 12:02:14 +08:00
walon 9083915b62 【重构】重构proto JobController代码,添加新的代码生成不再需要修改JobController代码。 2021-11-26 19:03:03 +08:00
walon 675f9954cf 【调整】cfg生成的go代码的包名由固定cfg改为由DefAssembly.TopModule 2021-11-26 18:13:54 +08:00
walon fa971c0e06 【优化】优化type的属性解析,可以正确去掉多余括号 2021-11-25 09:52:26 +08:00
walon 41fd0a908b 【优化】优化Dockerfile,选用cache加速生成 2021-11-25 09:52:26 +08:00
walon 3a021f74a8 Set theme jekyll-theme-slate 2021-11-24 15:32:46 +08:00
walon 8cd45782f4 【优化】优化type的attr解析,允许用()来界定属性的定义范围,如int#(set=1,2,3)
【优化】模块中可以通过assembly属性获得DefAssembly变量
2021-11-22 16:39:32 +08:00
walon 5e56d35e4a 【特性】新增validator set, 要求值必须在某一集合内
【重构】重构validator,基于注解获得所有Validator类
2021-11-22 15:50:05 +08:00
walon f024ec6974 【调整】移除特殊tag: no,不再用于表示不导出记录 2021-11-20 15:55:08 +08:00
walon 35082d96d5 【新增】新增特殊tag: unchecked。 校验器不检查带此tag的记录。 2021-11-20 15:31:51 +08:00
walon fcc12ab0d2 【更新】补充json和lua数据源文档中关于text类型的说明 2021-11-19 09:58:02 +08:00
walon 9eacac3171 【特性】新增 SizeValidator 2021-11-18 17:06:31 +08:00
walon 4f0addec89 【更新】完善json,lua 数据源文档 2021-11-18 13:23:14 +08:00
walon 21a745a4f7 【重构】 config/rust_json/mod_header.tpl 改名为 include.tpl 2021-11-17 10:19:15 +08:00
walon 8c02821fd8 【重构】将python_json生成代码中import和vector定义部分移到 include.tpl,方便定制 2021-11-17 10:15:31 +08:00
walon e9f608a238 【优化】convert_xlsx格式,对于复合字段,如果未指定sep,则默认使用| 2021-11-16 19:13:21 +08:00
walon 8e8fe68fd0 【更新】更新json、lua、yaml数据源文档 2021-11-16 16:12:14 +08:00
walon d9e7bc3914 【优化】考虑到##field容易写错,新增##+和##var作为子字段行的标识名 2021-11-16 12:30:12 +08:00
walon 9fd76d5334 【特性】lua,yaml格式也支持从子字段读取记录
【修复】修复json格式中读取字段列表时未判定忽略的null数据的bug
2021-11-16 09:41:50 +08:00
walon 2034e8050f 【调整】将多态类型id字段ID改名为__ID__,避免与常见的字段名ID冲突而产生编译错误 2021-11-15 23:47:50 +08:00
walon f6b8b32123 【修复】修复.net 6的TimeZone相关调整导致datetime计算错误的问题
【调整】新增datetime的等价类型名time
【调整】Luban.Server的Dockerfile不再拷备localtime文件
2021-11-15 23:26:17 +08:00
walon 9cc489ecfd 【特性】json数据源支持从json子字段读入bean或者list,bean,支持用*@xxx.json形式将json当作一个记录列表读入list,bean形式的数据 2021-11-15 22:27:49 +08:00
walon 390a72747d 【修复】修复解析excel数据出错时,打印行号有误的bug 2021-11-13 14:53:34 +08:00
walon a11380d142 【调整】Luban.ClientServer忽略-h参数,避免意外添加-h变成云生成,给新手造成很多困惑。 2021-11-10 22:25:04 +08:00
walon fd0817d630 【优化】缓存在excel中的定义的表ValueType结构,加速生成 2021-11-10 16:06:55 +08:00
walon 5a21c9b1b4 【调整】 cfg --output_data_dir 改为可选参数。只生成代码时可以不指定此参数
【更新】更新文档
2021-11-10 09:44:11 +08:00
walon afbfaa1c55 【调整】调整java_bin和java_json的生成代码,移除var关键字,兼容java 1.8的语法。 2021-11-09 18:40:58 +08:00
walon 28c6ee75a0 【升级】升级.trivis脚本中.net 5到.net 6 2021-11-09 11:05:55 +08:00
walon 66db58d3fb 【升级】升级到.net6 2021-11-09 10:08:49 +08:00
467 changed files with 14075 additions and 7776 deletions

View File

@ -2,7 +2,7 @@ language: csharp
solution: src/Luban.sln
mono: none
dotnet: 5.0
dotnet: 6.0
script:
- cd src
- dotnet restore Luban.sln

File diff suppressed because it is too large Load Diff

988
README.md

File diff suppressed because it is too large Load Diff

View File

@ -1 +1 @@
theme: jekyll-theme-cayman
theme: jekyll-theme-slate

View File

@ -1,126 +0,0 @@
* start up
- [下载 dotnet 5](https://dotnet.microsoft.com/download/dotnet/5.0)
- [下载 luban.client&luban.server](https://github.com/focus-creative-games/luban_examples/tree/main/Tools)
- 启动 luban.server
- 创建游戏配置
- root.xml
- 新增一个表定义
- 新增一个excel文件
- 生成
- c# dotnet core
- gen.client 命令行
- c# unity
- c# unity + ILRuntime
- lua unity xlua
- lua unity tolua
- 其他lua
- java
- go
- cpp
- typescript
- python 2.7及3.0
* 进阶
- 游戏配置
- excel 篇
- 标题头定义
- 定义和配置更多基础类型
- 定义枚举
- 定义bean
- 数据格式
- 指定某个或者多个sheet或者文件
- list 或者其他类型
- group 分组导出
- tag
- sep
- multi_rows
- 多级标题头
- 单例表
- 行表与列表
- 可空变量
- datetime
- convert
- 多态bean
- json 篇
- lua
- xml
- 一个同时包含 excel 、 json 配置的项目
- 数据校验
- ref
- path
- 导出格式
- bin 格式
- lua 格式
- 本地化
* gen.client & gen.server 工作模型
![](docs/images/Client_Server.png)
* 定义 + 数据的 抽象模型
* 定义
* 根定义文件
* group
* service
* topmodule
* import
* 子模块定义
* 类型系统
- 基础类型
- 可空类型
- 枚举
- const
- bean
- field
- convert 常量替换
- 多态
* table
* module
* 源数据格式
- excel
- json
- lua
- xml
* 导出数据格式
- bin
- json
- lua
- emmylua anntations
* input 多种数据文件来源
* group 数据分组
* tag
* 资源列表导出
* validator 数据检验
* 本地化
* editor 相关。导出符合luban数据约束的文件。
* ide支持
- emmylua anntations
- luban.client 命令行介绍
- luban.client 命令行参数
- job cfg 命令行参数
* 各个语言和引擎及平台下的项目搭建
- c# + dotnet core
- c# + unity
- c# + unity + ILRuntime
- 其他 c# 平台
- lua + unity + tolua
- lua + unity + xlua
- lua + unity + slua
- lua + unreal + unlua
- lua + cocos2dx
- 其他使用 lua 的 平台
- c++ + unreal
- c++ + 其他平台
- go
- java
- python 2.7
- python 3.0
- typescript + 微信小游戏
- typescript + 其他平台
* luban 开发
- git clone 项目
- luban 构建与发布
- 普通构建
- docker
- 代码结构介绍
- 自定义 job

View File

@ -1,409 +0,0 @@
[//]: # "Author: bug"
[//]: # "Date: 2020-11-01 15:40:11"
## bool 类型
用 true 或 false 表示 bool 值,只需要小写后是这两个值即可,比如 true,True,True 都是合法的值。excel 会自动将输入的值大写化。
|##| id | x|
|-|-|-|
|##type|int| bool|
|| 1| true|
||2| false |
## float 类型
|##| id | x|
|-|-|-|
|##type|int| float|
|| 1| 1.2|
||2| 4.895 |
## byte,short,int,long,string
与 float 相似,不再赘述
## string 类型
填法与 float相似但又特殊性。当string类型数据出现在**连续单元格**比如多列bean占了多个单元格或者用**sep分割**的数据中时,由于工具无法区别空白到底算空白字符串还是应该跳过,所以在连续多字段模式下,强制用""来表示空白字符串。
下面示例中s1是string单独占一个列可以用空白单元格表示空白字符串。 cs1和cs2要么是连续单元格模式要么是sep分割产生的连续数据模式表达空白字符串必须用""。
可能会有疑问,如何恰好想表达 ""是怎么办呢? 如果真遇到这种需求,再寻找解决方案吧。
|##| id | x|
|-|-|-|
|##type|int| string|
|| 1| aaabbb|
||2| |
||3| ""|
## text 类型
该类型数据包含两个字段, key和text 其中 key 可以重复出现但要求text完全相同否则报错。这么设计是为了防止意外写重了key。**注意不允许key为空而text不为空**
如果想填空的本地化字符串, key和text完全留空即可工具会特殊对待不会加入 key集合。
text的key和text字段都是string类型因此在连续单元格或者sep产生的连续数据流模式中同样要遵循用""来表达空白字符串的规则。
|##| id | x&sep=|
|-|-|-|
|##type|int| text|
|| 1| /demo/key1#aaaa|
||2| /demo/key2#bbbb|
||3| | |
## 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内置的时间格式填写
|##| id | x|
|-|-|-|
|##type|int| datetime|
|| 1|1999-09-09 01:02:03|
||2| 1999-09-09 01:02|
||3| 1999-09-09 01 |
||4| 1999-09-09|
## 可空变量
- 有时候会有一种变量,我们希望它 功能生效时填一个有效值,功能不生效里,用一个值来表示。 例如 int 类型,常常拿 0 或者-1 作无效值常量。 但有时候0 或-1 也是有效值时,这种做法就不生效了。或者说 项目组内 有时候拿 0有时候拿-1 作无效值标记,很不统一。我们借鉴 sql 及 c#,引入 可空值概念,用 null 表达空值。
|##| id | x| color |
|-|-|-| - |
|##type|int| int?|QualityColor?|
|| 1| 1| A |
||2| null|B|
||3| 2|null|
## 列表类型 list,int
|##| id | x|
|-|-|-|
|##type|int| list,int|
|| 1| 12,33,44|
||2| 10,20,30,40|
## 向量类型 vector2,vector3,vector4
vector3 有三个字段 float x, float y, float z, 适合用于表示坐标之类的数据。
|##| id | x2|x3|x4|
|-|-|-| -| -|
|##type|int| vector2|vector3|vector4|
|| 1| 1,2|11,22,33|12,33,44,55|
||2| 2,3|22,44,55|6.5,4.7,8.9|
## 枚举类型
- 道具有品质,白绿蓝紫橙。 虽然可以直接填 1-5 这几个数据,但不直观,而且对程序非常不友好。
- 有一种更优雅的方式是定义枚举。
- [枚举定义](images/adv/def_09.png)
``` xml
<enum name = "EQuality">
<var name = "WHITE" alias = "白" value = "1">
<var name = "GREEN" alias = "绿" value = "2">
<var name = "BLUE" alias = "蓝" value = "3">
<var name = "PURPLE" alias = "紫" value = "4">
<var name = "ORANGE" alias = "橙" value = "5">
</enum>
```
- 之前用 bean 来定义结构,我们引入的新的 tag “enum” 来定义 枚举。
- enum 的 name 属性表示 枚举名。
- 如果生成 c#代码的话,会生成 cfg.item.Equality 类。
- `<var name=”xxx” alias=”xx” value=”xx”/>` 为检举项。
- 其中 name 为必填项,不可为空,也不能重复。
- alias 是可选项,枚举项别名。
- value 是枚举值,主要给程序使用。
- [完整用法](images/adv/def_10.png)
``` xml
<module name = "item">
<enum name = "EQuality">
<var name = "WHITE" alias = "白" value = "1">
<var name = "GREEN" alias = "绿" value = "2">
<var name = "BLUE" alias = "蓝" value = "3">
<var name = "PURPLE" alias = "紫" value = "4">
<var name = "ORANGE" alias = "橙" value = "5">
</enum>
<bean name = "Item">
<var name = "quality" type = "EQuality">
</bean>
</module>
```
- excel 表中,想表达一个枚举值,既可以用检举项 name,也可以用枚举项的 alias但不能是相应的整数值。
- 注意!如果想引用其他模块定义的 enum 或者 bean, type 里必须指定全名。
比如 type=”mall.TradeInfo” 。
|##| id | color |
|-|-| - |
|##type|int| QualityColor?|
|| 1| GREEN |
||2| RED|
||3| 白|
## bean 类型
- 有时候希望一个字段是复合类型。
- 比如,我们想用一个字段 level_range 来表示道具可以使用的等级范围,它包含两个字段,最小等级和最大等级。
- 此时,可以通过定义 bean 来解决。
[定义](images/adv/def_12.png)
``` xml
<bean name="IntRange">
<var name="min" type="int">
<var name="max" type="int">
</bean>
<bean name="Item">
<var name="level_range" type="IntRange">
</bean>
```
- 之前的字段都在一个单元格内填写,现在这个字段是 bean 类型,有两个值,该怎么填写呢?
如果也像 list,int 那样把两个数写在一个单元格里(如下图),会发现工具报错了: “10,20” 不是合法的整数值。
![如图](images/adv/def_13.png)
- 填写这些占据多个单元格的数据有两种办法:
1. 合并标题头
让 level_range 标题头占据两列,这样就能在两个单元格里分别填写最小最大等级了。
![如图](images/adv/def_14.png)
2. 使用 sep 分割单元格
字段定义中指定 sep 属性,用某些字符分割单元格,这样就能识别为两个整数了。
[定义](images/adv/def_15.png)
``` xml
<bean name="IntRange">
<var name="min" type="int">
<var name="max" type="int">
</bean>
<bean name="Item">
<var name="level_range" type="IntRange" sep="|">
</bean>
```
如果想用 分号; 或者 竖号 | 分割,只要 sep=”;” 或者 sep=”|“ 即可。
![如图](images/adv/def_16.png)
## list,bean 类型
- 有些时候,我们需要一个 结构列表字段。
比如说 道具不同等级会增加不同的血量。我们定义一个 ItemLevelAttr 结构。
[定义](images/adv/def_17.png)
``` xml
<module name="item">
<bean name="ItemLevelAttr">
<var name="level", type="int">
<var name="desc", type="string">
<var name="attr", type="float">
</bean>
<bean name="Item">
<var name="level_attrs" type="list,ItemLevelAttr" />
</bean>
</module>
```
配置:
![如图](images/adv/def_18.png)
- 对于多个值构成的字段,填写方式为 在标题头(level_attrs)对应的列范围内,按顺序填值。不需要跟策划的标题头名有对应关系。空白单元格会被忽略。也就是如下填法也是可以的:
![如图](images/adv/def_19.png)
- 这种填法的缺点是占据在太多的列。如果想如下填,该怎么办呢?
![如图](images/adv/def_20.png)
- 有两种办法。
1. bean ItemLevelAttr 增加属性 sep=”,”
[定义](images/adv/def_21.png)
``` xml
<bean name="ItemLevelAttr" sep=",">
<var name="level" type="int"/>
<var name="desc" type="string"/>
<var name="attr" type="float"/>
</bean>
```
如果不想用逗号”,” ,想用;来分割单元格内的数据,只要将 sep=”;” 即可。
2. 字段 level_attrs 增加属性 sep=”,”,即
[定义](images/adv/def_22.png)
``` xml
<bean name="ItemLevelAttr" sep=",">
<var name="level" type="int"/>
<var name="desc" type="string"/>
<var name="attr" type="float"/>
</bean>
<bean name="Item">
<var name="level_attrs" type="list,ItemLevelAttr" sep=",">
</bean>
```
如果想所有数据都在一个单元格内填写,又该怎么办呢?
![如图](images/adv/def_23.png)
想用 | 来分割不同 ItemLevelAttr ,用 , 来分割每个记录的数据。只需要 字段 level_attrs 的 sep=”,|” 即可。
[定义](images/adv/def_24.png)
``` xml
<bean name="ItemLevelAttr" sep=",">
<var name="level" type="int"/>
<var name="desc" type="string"/>
<var name="attr" type="float"/>
</bean>
<bean name="Item">
<var name="level_attrs" type="list,ItemLevelAttr" sep=",|">
</bean>
```
## 多态 bean
- 多态 bean 的 Luban 类型系统的核心,没有它就不可能比较方便简洁地表达游戏内的复杂数据。
- 常见的结构都是固定,但有时候会有需求,某个字段有多种类型,每种类型之间可能有一些公共字段,但它们也有一部分不一样的字段。简单的做法是强行用一个结构包含所有字段,这在类型种类较少时还勉强能工作,但类型很多,字段个数变化极大时,最终的复合结构体过于庞大笨拙,故而难以在实际采用。
- Luban 引入了 OOP 中类型继承的概念,即多态 bean。方便表达那些复杂配置需求。
- 假设 item 有一个形状 Shape 类型的字段。Shape 有两种 Circle 和 Rectangle.
Cicle 有 2 个字段 int id; float radius;
Rectangle 有 3 个字段 int id; float width; float height;
[定义](images/adv/def_25.png)
``` xml
<bean name="Shape">
<var name="id" type="int">
<bean name="Circle">
<var name="radius" type="float">
</bean>
<bean name="Rectangle">
<var name="width" type="float"/>
<var name="height" type="float"/>
</bean>
</bean>
<bean name="Item">
<var name="shape" type="Shape"/>
</bean>
```
配置:
![如图](images/adv/def_26.png)
- 注意到,多态 bean 与普通 bean 填写区别在于,多态 bean 需要一个类型名。这也好理解,如果没有类型名,如何知道使用哪个 bean 呢。
- 有时间策划不太习惯填写英文,或者说类型名有时候会调整,不希望调整类型名后配置也跟着改变,因为,多态 bean 支持别名的概念。
[定义](images/adv/def_27.png)
``` xml
<bean name="Shape">
<var name="id" type="int"/>
<bean name="Circle" alias="圆">
<var name="radius" type="float"/>
</bean>
<bean name="Rectangle" alias="长方形">
<var name="width" type="float"/>
<var name="height" type="float"/>
</bean>
</bean>
```
- 配置
![如图](images/adv/def_28.png)
- 使用类型名和别名来标识多态都是支持的,可以混合使用。
## multi_rows 多行 记录
- 使用数据表经常会遇到某个字段是列表类型的情形。有时候列表的 bean 的字段特别多,比如多达 10 个字段,列表包含了好几个 bean。如果此时配置成一行会导致 excel 列过多,策划编辑维护不方便直观。 Luban 支持这个列表 多行配置。
- [定义](images/adv/def_29.png)
``` xml
<bean name="MultiLineType">
<var name="x1" type="int"/>
<var name="x2" type="int"/>
<var name="x3" type="int"/>
<var name="x4" type="int"/>
<var name="x5" type="int"/>
<var name="x6" type="int"/>
<var name="x7" type="int"/>
</bean>
<bean name="Item">
...
<var name="lines" type="list,MultiLineType" multi_rows="1"/>
</bean>
```
- 和 普通 非多行记录的区别在于 lines 字段多了一个 multi_rows=”1” 字段,表示这个字段要多行配置。
- ![如图](images/adv/def_30.png)
## 多级标题头
- 经常会有字段占了多列,比如 Shape, 如果按顺序填写,有个问题在于,字段很多时,容易对应错误,不方便定位。
- 假设 有个 show_info 字段,包含 如下字段 string name; string desc; string tip;
- [定义](images/adv/def_31.png)
``` xml
<bean name="ShowInfo">
<var name="name" type="string" />
<var name="desc" type="string" />
<var name="tip" type="string" />
</bean>
<bean name="Item">
...
<var name="show_info" type="ShowInfo"/>
</bean>
```
- 配置
![如图](images/adv/def_32.png)
- 有几处改动
1. 我们新插入了一行标题头,第 2 行变成了两行。同时 A2,A3 单元格合并,表示标题头占了 2 行。
2. show_info 下一行,填上 子字段名 (顺序不重要)
- 我们称之为多级标题头,通过多级标题头的方式,可以精确定位深层次字段的列。方便策划填。
## 单例表
- 不是所有数据都是 类似 map 这样的多记录结构。有些配置只有一份,比如 开启装备系统的最小角色等级。 这些数据 所在的表,也只有一个记录。称之为 单例表。
- 我们创建一个单例表,来存放这些数据。
- [定义](images/adv/def_33.png)
``` xml
<bean name="GlobalConfig">
<var name="unlock_equip_sys_level" type="int"/>
<var name="unlock_mall_sys_level" type="int"/>
</bean>
<table name="TbGlobalConfig" value="GlobalConfig" mode="one" input="item/全局参数表.xlsx"/>
```
- 配置
![如图](images/adv/def_34.png)
## 横表与纵表
- 之前介绍的表都是 面向行,沿着行方向填写数据。有时候我们希望 以列为方向填写。
- 比如 上面的单例表。 如果改成一行一个字段,看起来会更清楚。 我们引入纵表支持。
- 定义不变,但 excel 的填法有区别,数据如下:
- ![如图](images/adv/def_35.png)
## 默认值
该特性只对excel格式文件有效。当单元格为空时该字段使用默认值。
```xml
<bean name="DemoDefault">
<var name="id" type="int"/>
<var name="x" type="int" default="10">
</bean>
<table name="TbDemoDefault" value="DemoDefault" input="default.xlsx"/>
```
![default](images/adv/def_50.png)
## convert 常量替换
游戏里经常会出现一些常用的类似枚举的值,比如说 升级丹的 id,在很多地方都要填,如果直接它的道具 id,既不直观,也容易出错。 Luban 支持常量替换。对于需要常量替换的字段,添加 convert=”枚举类”。 如果填写的值是 枚举名或者别名,则替换为 相应的整数。否则 按照整数解析。
定义
``` xml
<enum name="EFunctionItemId">
<var name="SHENG_JI_DAN" alias="升级丹" value="11220304"/>
<var name="JIN_JIE_DAN" alias="进阶丹" value="11220506"/>
</enum>
<bean name="Item">
<var name="cost_item_on_use" type="int" convert="EFunctionItemId"/>
</bean>
```
配置:
![如图](images/adv/def_41.png)

View File

@ -1,57 +0,0 @@
[//]: # (Author: bug)
[//]: # (Date: 2020-11-01 16:26:41)
### json 数据源
```xml
<bean name="DemoType2" >
<var name="x4" type="int" convert="DemoEnum"/>
<var name="x1" type="bool"/>
<var name="x5" type="long" convert="DemoEnum"/>
<var name="x6" type="float"/>
<var name="x7" type="double"/>
<var name="x10" type="string"/>
<var name="x12" type="DemoType1"/>
<var name="x13" type="DemoEnum"/>
<var name="x14" type="DemoDynamic" sep=","/>多态数据结构
<var name="v2" type="vector2"/>
<var name="v3" type="vector3"/>
<var name="v4" type="vector4"/>
<var name="t1" type="datetime"/>
<var name="k1" type="array,int"/> 使用;来分隔
<var name="k2" type="list,int"/>
<var name="k8" type="map,int,int"/>
<var name="k9" type="list,DemoE2" sep="," index="y1"/>
<var name="k15" type="array,DemoDynamic" sep=","/>
</bean>
<table name="TbDataFromJson" value="DemoType2" input="test/json_datas"/>
```
递归遍历test/json_datas整个目录树**按文件名排序后**依次将每个json数据当作一个记录读入。其中1.json文件内容如下
```json
{
"x1":true,
"x2":3,
"x3":128,
"x4":1,
"x5":11223344,
"x6":1.2,
"x7":1.23432,
"x10":"hq",
"x12": { "x1":10},
"x13":"B",
"x14":{"__type__": "DemoD2", "x1":1, "x2":2},
"v2":{"x":1, "y":2},
"v3":{"x":1.1, "y":2.2, "z":3.4},
"v4":{"x":10.1, "y":11.2, "z":12.3, "w":13.4},
"t1":"1970-01-01 00:00:00",
"k1":[1,2],
"k2":[2,3],
"k7":[2,3],
"k8":[[2,2],[4,10]],
"k9":[{"y1":1, "y2":true},{"y1":2, "y2":false}],
"k15":[{"__type__": "DemoD2", "x1":1, "x2":2}]
}
```

View File

@ -1,65 +0,0 @@
[//]: # (Author: bug)
[//]: # (Date: 2020-11-01 16:26:41)
# Lua 数据
##
* 与 json 相似定义。
* 唯一区别在于, lua 的table的key支持任意格式所以 lua 的map 可以直接 {[key1] = value1, [key2] = value2, ,,,}
* ![如图](images/adv/def_42.png)
* 注意 数据前有一个 return 语句。因为 lua 数据是当作 lua 文件加载的,每个加载后的结果当作一个记录读入。
```xml
<bean name="DemoType2" >
<var name="x4" type="int" convert="DemoEnum"/>
<var name="x1" type="bool"/>
<var name="x5" type="long" convert="DemoEnum"/>
<var name="x6" type="float"/>
<var name="x7" type="double"/>
<var name="x10" type="string"/>
<var name="x12" type="DemoType1"/>
<var name="x13" type="DemoEnum"/>
<var name="x14" type="DemoDynamic" sep=","/>多态数据结构
<var name="v2" type="vector2"/>
<var name="v3" type="vector3"/>
<var name="v4" type="vector4"/>
<var name="t1" type="datetime"/>
<var name="k1" type="array,int"/> 使用;来分隔
<var name="k2" type="list,int"/>
<var name="k8" type="map,int,int"/>
<var name="k9" type="list,DemoE2" sep="," index="y1"/>
<var name="k15" type="array,DemoDynamic" sep=","/>
</bean>
<table name="TbDataFromLua" value="DemoType2" input="test/lua_datas"/>
```
递归遍历test/lua_datas整个目录树**按文件名排序后**依次将每个lua数据当作一个记录读入。其中1.lua文件内容如下
```lua
return
{
x1 = false,
x2 = 2,
x3 = 128,
x4 = 1122,
x5 = 112233445566,
x6 = 1.3,
x7 = 1122,
x10 = "yf",
x12 = {x1=1},
x13 = "D",
x14 = { __type__="DemoD2", x1 = 1, x2=3},
v2 = {x= 1,y = 2},
v3 = {x=0.1, y= 0.2,z=0.3},
v4 = {x=1,y=2,z=3.5,w=4},
t1 = "1970-01-01 00:00:00",
k1 = {1,2},
k2 = {2,3},
k8 = {[2]=10,[3]=12},
k9 = { {y1=1,y2=true}, {y1=10,y2=false} },
k15 = { { __type__="DemoD2", x1 = 1, x2=3} },
}
```

View File

@ -1,64 +0,0 @@
[//]: # (Author: bug)
[//]: # (Date: 2020-11-01 16:26:41)
### json 数据源
```xml
<bean name="DemoType2" >
<var name="x4" type="int" convert="DemoEnum"/>
<var name="x1" type="bool"/>
<var name="x5" type="long" convert="DemoEnum"/>
<var name="x6" type="float"/>
<var name="x7" type="double"/>
<var name="x10" type="string"/>
<var name="x12" type="DemoType1"/>
<var name="x13" type="DemoEnum"/>
<var name="x14" type="DemoDynamic" sep=","/>多态数据结构
<var name="v2" type="vector2"/>
<var name="v3" type="vector3"/>
<var name="v4" type="vector4"/>
<var name="t1" type="datetime"/>
<var name="k1" type="array,int"/> 使用;来分隔
<var name="k2" type="list,int"/>
<var name="k8" type="map,int,int"/>
<var name="k9" type="list,DemoE2" sep="," index="y1"/>
<var name="k15" type="array,DemoDynamic" sep=","/>
</bean>
<table name="TbDataFromXml" value="DemoType2" input="test/xml_datas"/>
```
递归遍历test/xml_datas整个目录树**按文件名排序后**依次将每个xml数据当作一个记录读入。其中1.xml文件内容如下
```xml
<data>
<x1>true</x1>
<x2>4</x2>
<x3>128</x3>
<x4>1</x4>
<x5>112233445566</x5>
<x6>1.3</x6>
<x7>1112232.43123</x7>
<x10>yf</x10>
<x12> <x1>1</x1> </x12>
<x13>C</x13>
<x14 __type__="DemoD2"> <x1>1</x1> <x2>2</x2> </x14>
<v2>1,2</v2>
<v3>1.2,2.3,3.4</v3>
<v4>1.2,2.2,3.2,4.3</v4>
<t1>1970-01-01 00:00:00</t1>
<k1> <item>1</item> <item>2</item> </k1>
<k2> <item>1</item> <item>2</item> </k2>
<k8>
<item> <key>2</key><value>10</value></item>
<item> <key>3</key><value>30</value></item>
</k8>
<k9>
<item> <y1>1</y1> <y2>true</y2> </item>
<item> <y1>2</y1> <y2>false</y2> </item>
</k9>
<k15>
<item __type__="DemoD2"> <x1>1</x1> <x2>2</x2> </item>
</k15>
</data>
```

View File

@ -1,87 +0,0 @@
[//]: # (Author: bug)
[//]: # (Date: 2020-11-01 16:26:41)
### json 数据源
```xml
<bean name="DemoType2" >
<var name="x4" type="int" convert="DemoEnum"/>
<var name="x1" type="bool"/>
<var name="x5" type="long" convert="DemoEnum"/>
<var name="x6" type="float"/>
<var name="x7" type="double"/>
<var name="x10" type="string"/>
<var name="x12" type="DemoType1"/>
<var name="x13" type="DemoEnum"/>
<var name="x14" type="DemoDynamic" sep=","/>多态数据结构
<var name="v2" type="vector2"/>
<var name="v3" type="vector3"/>
<var name="v4" type="vector4"/>
<var name="t1" type="datetime"/>
<var name="k1" type="array,int"/> 使用;来分隔
<var name="k2" type="list,int"/>
<var name="k8" type="map,int,int"/>
<var name="k9" type="list,DemoE2" sep="," index="y1"/>
<var name="k15" type="array,DemoDynamic" sep=","/>
</bean>
<table name="TbDataFromYaml" value="DemoType2" input="test/yaml_datas"/>
```
递归遍历test/yaml_datas整个目录树**按文件名排序后**依次将每个yaml数据当作一个记录读入。其中1.yml文件内容如下
```yaml
---
x1: true
x2: 3
x3: 128
x4: 40
x5: 11223344
x6: 1.2
x7: 1.23432
x10: hq
x12:
x1: 10
x13: B
x14:
__type__: DemoD2
x1: 1
x2: 2
s1:
key: "/key32"
text: aabbcc22
v2:
x: 1
y: 2
v3:
x: 1.1
y: 2.2
z: 3.4
v4:
x: 10.1
y: 11.2
z: 12.3
w: 13.4
t1: '1970-01-01 00:00:00'
k1:
- 1
- 2
k2:
- 2
- 3
k8:
- - 2
- 2
- - 4
- 10
k9:
- y1: 1
y2: true
- y1: 2
y2: false
k15:
- __type__: DemoD2
x1: 1
x2: 2
```

View File

@ -1,20 +0,0 @@
[//]: # (Author: bug)
[//]: # (Date: 2020-11-23 00:21:21)
# 服务端布署
## Docker 镜像布署
- 镜像会自动更新至最新版
- 在有 docker 的环境下,执行下面的指令
``` bash
docker pull hugebug4ever/luban:latest
docker run --name luban-server -d -p 8899:8899 hugebug4ever/luban:latest
```
## Windows 下命令行布署
- 下载 [release](https://github.com/focus-creative-games/luban/releases) 版本
- 解压 Luban.Server.zip
- 执行下面的命令即可
```
Luban.Server.exe
```

View File

@ -1,26 +0,0 @@
[//]: # "Author: bug"
[//]: # "Date: 2020-11-15 23:59:21"
===============================================================================
## 为什么要使用"客户端/服务器"模式
- 最初的想法是服务端闭源,因为代码生成部分花了很多心思,也方便加密之类的操作
- 为了方便与社区开发者交流开源后,还保留了这一模式,因为觉得
- 这一模式,也能作为 ddc 的基础 (distributed data cache)
- 配置文件需要反复生成与测试
- 文件数量上升后,配置生成不可避免地变慢
- 再快的技术,也挡不住大量数据的处理
## 如果用客户端生成,再通过服务器端分享缓存,不是更快么
- 是的。
- 但服务器模式还有方便更新的优势,就像网页访问,不需要每个客户端升级浏览器。
- 通知每个使用者要更新本地目录这件事,在实践中也产生过很多麻烦。
- 如果有效率提升,更佳的 feature (比如统计?), 在服务端实施更方便。
- c-s 本身是个流行的设计,虽然万物皆可客户端 ^_^。
- 考虑至此,就没有继续深究这个问题。
## 为什么 excel 的配置不像别的工具,用文件头描述数据?
- 数据描述除了 excel 还可以为 json 等其它数据源。用 xml 可以更统一。
- 数据类型可以描述地更详细,毕竟 excel 的表达只是强在数据。
- 数据关系可以更好地表达,比在 excel 中用一些技巧实现更舒服。

View File

@ -1,199 +0,0 @@
[//]: # (Author: bug)
[//]: # (Date: 2020-10-18 15:35:26)
## 特性说明
--------------------
### 支持的数据类型
* 基础内置类型
- bool,byte,short,fshort,int,fint,long,flong,float,double,string,text,bytes
- vector2, vector3,vector4
- datetime
* 可空类型
- bool?,byte?,short?,fshort?,int?,fint?,long?,flong?,float?,double?
- vector2?,vector3?,vector4?
- datetime?
* 自定义枚举 enum
* 自定义常量 const
* 自定义结构 bean
- 可以定义所有类型的字段
- 支持无限制的结构继承,对于表达中大型项目的复杂数据(技能,buff,ai 等等) 极其有用。 (比如基类Shape, 子类 Circle,Rectangle
```mermaid
graph TD;
A-->B;
A-->C;
B-->D;
B-->E;
```
* 支持容器类型 array。 value 可以为内置类型,也可以为自定义类型
* 支持容器类型 list。 value 可以为内置类型,也可以为自定义类型
* 支持容器类型 set。 value 只能为内置类型或者enum类型不支持 bean 类型
* 支持容器类型 map。 key 只能为内置类型或者enum类型不支持 bean 类型。 value 可以为内置类型,也可以为自定义类型
### 多数据源支持
* 支持excel族。 csv 及 xls,xlsx等格式
* 支持从指定excel里的某个单元薄读入。
* 支持json。 每个json文件当作一个记录读入
* 支持lua。 每个lua文件当作一个记录读入
* 支持xml。 每个xml文件当作一个记录读入
* 支持目录。 递归目录下的所有文件每个文件当作一个记录读入。允许不同类型的文件混合比如目录下可以同时有json,lua,xml,excel之类的格式。
* 允许指定多个数据源,可以使用以上所有的组合。
* 扩展新的数据源也非常容易 (像支持lua,json,xml数据源只用了200行左右代码)
### 多种数据表模式
* one 格式,即单例表模式
* map 格式即普通key-value表模式。 任何符合set 的value要求的类型都可以做key
* bmap 格式,即双主键模式。 任何符合 set 的value要求的类型都可以作 key1和key
### 分组导出
在大多数项目中,导出给前后端的数据并非完全相同。有些表可能仅仅前端或者后端需要,有些字段也可能仅仅前端或者后端需要。 luban同时支持两种级别的分组
#### 表级别分组
定义方式为在table中定义group属性如果未定义 group,则默认导出给所有分组如果定义group则只导出给指定分组可以多个以逗号","分隔。
例如: TbDemoGroup_C表只给客户端使用, TbDemoGroup_S只能服务器使用, TbDemoGroup_E只给editor使用。
定义如下:
![group_table](docs/images/examples/group_02.png)
#### 字段级别分组
定义方式为给var指定group属性未指定则默认导出给所有分组。可以为多个以逗号","分隔。相比于大多数导表工具只支持**表顶级字段**的分组导出luban支持任意bean字段粒度级别的分组导出。
例如, TbDemoGroup表中 id,x1,x4 字段前后端都需要; x3 只有后端需要;x2 字段只有前端需要。x5是bean类型它导出给前后端但它的子字段也可以被分组过滤 x5.y1, x2.y4前后端都会导出x5.x3只导出给后端,x5.x2只导出给前端。
定义如下:
![group_var](docs/images/examples/group_01.png)
### 生成极快
* 大项目几十M配置数据也能1秒导出
* 生成工具使用客户端/服务器架构
* 服务器使用多线程加速生成,数十倍提高生成速度
* 服务器使用缓存直接返回未改动的代码或者数据的生成结果
* 支持增量生成
### 增强的 excel 格式
* 支持填写任意复杂的数据(比如 常见的 list,bean 这种类型)
* 支持 sep 在单个单元格内填写多个数据
* 支持 多行数据。即 对于 list,bean 类型的字段,可以多行填写
* 支持多级标题头,对填写 层次很深的数据时非常有用
* 支持导出标记。比如 是、否、test 等等,用于标记某行数据是否导出
* 支持用 true,false表示 bool 值,语义更清晰。
* 支持枚举 alias 别名,填写枚举类型不再需要写数字了
* 支持可空变量。可以用null表达空。 某些场合下语义更清晰。
* 支持 datetime 数据类型. 时间格式标准为以下几种最终被转化为utc时间方便程序处理
- yyyy-MM-dd HH:mm:ss
- yyyy-MM-dd HH:mm
- yyyy-MM-dd HH
- yyyy-MM-dd
### 代码编辑器支持
* 根据配置表定义生成相应的json文件的 load 及 save 代码(c#或者c++)方便编辑器加载和导出。每个记录对应一个json文件。
* 支持 unity 下用c# 开发编辑器
* 支持 unreal 下用c++ 开发的编辑器
### 多数据源
支持表数据来自excel文件来自excel某个单元薄来自json、xml、yaml文件来自目录下所有文件。以及以上几种的组合。
#### 来自某个excel文件
```xml
<table name="TbItem" value="Item" input="item/item1.xlsx">
```
#### 来自某个excel单元薄
```xml
<table name="TbItem" value="Item" input="table1@item/item1.xlsx">
```
####
#### 一个数据表来自两个excel文件
通过 excel文件1,excel文件2... 的方式指定数据表的数据来自多个文件,不同文件以逗号","分隔。当数据源为excel文件并且没有用@来指定某个单元表时该excel文件的中的所有单元表都会被读入。例如TbItem表的数据来自item目录下的item1.xlsx和item2.xlsx。
```xml
<table name="TbItem" value="Item" input="item/item1.xlsx,item/item2.xlsx">
```
#### 两个数据表来自同一个excel文件的不同单元表
通过 <单元表名>@excel文件的方式指定数据来自excel文件的某个单元表可以指定多个单元表通过逗号","分隔。示例中TbItem占了table1、table3两个单元表TbEquip占了table2、table4两个单元表。同一个数据表占据的单元表不必连续。示例中故意让TbItem和TbEquip占了不相邻的两个单元表。
```xml
<table name="TbItem" value="Item" input="table1@examples.xlsx,table3@examples.xlsx">
<table name="TbEquip" value="Equip" input="table2@examples.xlsx,table4@examples.xlsx">
```
#### 一个数据表的数据来自**目录**下的所有文件
当以目录为数据源时,会遍历整个目录树中所有文件,除了文件名以 ",.~"字符逗号或点号或波浪号开头的文件外读入每个文件中的数据。如果是excel族的数据会从每个文件中读取多个记录如果是xml、lua、json族的数据每个文件当作一个记录读入。 可以有指定多个目录同时为数据源,以逗号","分隔。
```xml
<table name="TbSkill" value="Skill" input="skill_datas">
```
### 支持多种导出数据格式
* **导出格式与源数据解耦**。无论源数据是 excel、lua、xml、json 或者它们的混合, 最终都被以统一的格式导出,极大简化了生成代码的复杂性。
* 导出binary。 内置binary格式加载最快占空间最少。
* 导出json 格式
* 导出 lua 格式
* 非常容易扩展其他输出格式(一般来说不到300行代码)
### 本地化支持
* 支持**本地化时间**。 配置中的 datetime会根据指定的timezone及localtime转化为正确的utc时间方便程序处理
* 支持**静态本地化**。 配置中的text类型在导出时已经转化为正确的本地化后的字符串
* 支持**动态本地化**。 配置中的text类型能运行时全部切换为某种本地化后的字符串
### 代码编辑器支持
* 支持 emmylua anntations。生成的lua包含符合emmylua 格式anntations信息。配合emmylua有强大的配置代码提示能力。
### 强大的数据校验能力
* 完整的数据内建约束检查
* ref 检查。检查表引用合法性。
* path 检查。检查资源引用合法性,对于防止策划填错极其有用,不再需要运行时才发现资源配置错误了。
* range 检查。检查数值范围。
* 扩展的自定义检查。使用自定义代码进行高级检查。提交配置前就能完成本地检查,避免运行时才发现错误,极大减少迭代成本。
### 资源导出支持
* 支持 res 资源标记。可以一键导出配置中引用的所有资源列表(icon,ui,assetbundle等等)
### 优秀的代码生成
- 良好模块化。比如对于c#语言生成cfg.item.Item,cfg.equip.EquipInfo这样的类
- 内存性能优化。支持c#值类型以及lua的紧凑格式保存数据节约内存。
- 支持为ref的字段生成resolve后的字段定义及加载后设置。读取所引用的字段不用再查表了。
- 支持对 list 类型数据 生成 index, 方便按列表和索引方式访问的需求。
### 良好的数据组织
- 数据模块化。允许策划按模块目录自由组织数据。
- 数据多源化。允许策划按需求选择最合适的源数据类型。
### 支持的语言 (覆盖主流的语言)
* 支持 c# (所有 .net framework 2 及以上, .net core平台)
* 支持 java (java 1.6 及以上)
* 支持 c++ ( c++ 11 及以上)
* 支持 go
* 支持 lua (5.1 及以上)
* 支持 typescript
* **新增其他语言支持非常容易**
### 支持的服务器引擎(满足语言版本的情况下)
* 纯 c# 的所有引擎
* 纯 java 的所有引擎
* 纯 go 的所有引擎
* 纯 c++ 的所有引擎
* 纯 lua 的所有引擎
* 纯 js或typescript的所有引擎
### 支持的客户端引擎(满足语言版本的情况下)
* unity + c#
* unity + tolua
* unity + xlua
* unity + ILRuntime
* cocosx-lua
* cocosx-js
* unreal + 纯 c++
* unreal + unlua
* unreal + puerts (typescript)
* 支持微信小程序和小游戏 sdk
* 支持 lua 的其他任何引擎
* 支持 js 或 typescript的 其他任何引擎
### 强大的扩展能力
* 支持插件形式,扩展其他生成极其方便

View File

@ -1,22 +0,0 @@
# 欢迎使用 Luban
## 介绍
Luban 是一个强大的生成与缓存工具。最初只用于对象生成对象可以是常规代码、配置数据、类似protobuf的消息代码也可以是游戏资源如assetbundle。
在大型项目中由于配置或资源数据庞大生成对象可能会花费相当多的时间。比如一个典型的MMORPG项目后期全量生成配置即使用了多线程加速所需时间
也在10秒的级别。因此使用client/server模式配合缓存机制来大幅加速生成过程。
Luban 最初是为了解决传统excel导出工具功能过于薄弱无法很好解决MMORPG游戏复杂配置需求的痛点问题。 自2015年以来经历过 MMORPG、卡牌、SLG 等多个上线项目的考验,
实际项目过程中不断迭代和优化,理解逐渐深入,最终由一个增强型的配置工具成为一个 **相对完备的游戏配置数据解决方案**。
## 文档
* [目录](docs/catalog.md)
## 支持和联系
QQ 群 : 692890842
QQ 群名 : Luban 开发交流群
邮箱 : taojingjian#gmail.com

View File

@ -1,157 +0,0 @@
## 本地化
支持多种本地化机制,分别处理不同的场合,它们可以同时使用。
### 静态文本值本地化
对于需要本地化的文本值,在配置导出时既已完成本地化的转化,适用于不同地区打不同包或者不同配置的情形。通过以下几个方面来实现静态文本值本地化:
- 使用 text 类型标识需要本地化的字符串。 text类型由两个字段构成, key和value。
- 使用本地化映射表提供text到其他语言的映射
- 未完成本地化的text单独输出到一个文件方便得知哪些文本值未完成本地化映射
#### 需要本地化的示例表
<table border="1">
<tr align="center">
<td>##</td>
<td>id</td>
<td colspan="2">name</td>
<td>desc</td>
<td>count</td>
</tr>
<tr align="center">
<td>##type</td>
<td>int</td>
<td colspan="2">text</td>
<td>string</td>
<td>int</td>
</tr>
<tr align="center">
<td/>
<td>1</td>
<td>/demo/key1</td><td>苹果</td>
<td>这是一个苹果</td>
<td>100</td>
</tr>
<tr align="center">
<td/>
<td>2</td>
<td>/demo/key2</td><td>香蕉</td>
<td>这是香蕉</td>
<td>100</td>
</tr>
<tr align="center">
<td/>
<td>3</td>
<td>/demo/key3</td><td>西瓜</td>
<td>这是西瓜</td>
<td>100</td>
</tr>
</table>
#### 文本值映射文件
| ## | key | origin_text | text_tw | text_en |
| - | - | - | - | - |
|##type|string|string|string|string|
|##|本地化key| 原始值 | 繁体值 | 英文 |
||/demo/key1|苹果|苹果|apple|
||/demo/key2|香蕉|香蕉|banana|
### Luban.Client 命令
有三个参数跟静态文本值本地化相关
- input_l10n_text_files 本地化映射文件
- l10n_text_field_name 映射的目标字段名。因为有可能多个语言都在同一个映射表内(如text_tw和text_en)
- output_l10n_not_translated_text_file 未完成文本值本地化映射的text
以下为一个示例脚本
```bat
%GEN_CLIENT% -h %LUBAN_SERVER_IP% -j cfg --^
-d %DEFINE_FILE%^
--input_data_dir %CONF_ROOT%\Datas ^
--output_code_dir Gen ^
--output_data_dir config_data ^
--gen_types data_json ^
-s all ^
--input_l10n_text_files l10n/cn/TextTable_CN.xlsx ^
--l10n_text_field_name text_en ^
--output_l10n_not_translated_text_file NotLocalized_CN.txt
```
### 示例输出结果
导出json文件内容为
```json
[
{
"id": 1,
"text": {
"key": "/demo/key1",
"text": "apple"
},
"desc": "这是一个苹果",
"count": 100
},
{
"id": 2,
"text": {
"key": "/demo/key2",
"text": "banana"
},
"desc": "这是香蕉",
"count":100
},
{
"id": 3,
"text": {
"key": "/demo/key3",
"text": "西瓜"
},
"desc":"这是西瓜",
"count": 100
}
]
```
示例中 /demo/key3 在本地化映射表中未提供因为output_l10n_not_translated_text_file 指定的未映射本地化文本值列表内容为。
```text
/demo/key3|这是西瓜
```
### 文本值动态本地化
运行时动态切换text类型字段到目标语言程序不需要根据id去本地化映射表里查询简化了使用。注意目前只有bean中text类型字段才支持像list,text之类的暂未支持。通过以下几个方面支持文本值动态本地化
- 标识字段为text类型
- 提供 (string key, string origin_value) => (string target_value) 的本地化映射函数
- 运行时调用 cfg.Tables.TranslateText函数一键切换配置中所有text类型值到目标语言
本地化映射函数实现比较简单,核心在于如何制作文本值本地化映射配置?使用者既可以使用自定义本地化映射表,可以使用普通的 luban配置表来提供 文本值映射配置,如下图:
|##| key | origin_text | text_tw | text_en |
|-| - | - | - | - |
|##type| string| string| string| string|
|##|本地化key| 原始值 | 繁体值 | 英文 |
||/demo/key1|苹果|苹果|apple|
||/demo/key2|香蕉|香蕉|banana|
假设想切换到en配置表的表名为 l10n.TbTextMapper示例c#版本地化映射函数如下:
```c#
string TextMapper(string key, string originText)
{
return tables.TbTextMapper.GetOrDefault(key)?.TextEn ?? originText;
}
```
### 多分支 数据
支持 main + patches的数据模式。在主版本数据基础上提供一个补丁数据合并处理后生成最终目标数据。适合制作海外有细节配置不同的多地区配置不需要
复制出主版本数据,接着在上面修改出最终数据。极大优化了制作本地化配置的工作流。
### 时间本地化
datetime类型数据在指定了本地化时区后会根据目标时区生成相应时刻的UTC时间方便程序使用.
通过Luban.Client的参数来指定时区
-t,--l10n_timezone <timezone>
该时区为linux下的时区名例如: -t "Asia/Shanghai" 指定目标时区为 亚洲上海(也即北京时间)

View File

@ -1,225 +0,0 @@
## 部属
Luban工具有两种部属方式。
### 方法1 Luban.Client与Luban.Server独立部属云生成模式
#### 部属 luban-server
- 基于 docker (强烈推荐以这种方式在服务器上部属luban-server因为可以充分利用缓存机制大幅缩短生成时间)
docker run -d --rm --name luban-server -p 8899:8899 focuscreativegames/luban-server:latest
- 基于 .net 5 runtime (可跨平台,不需要重新编译)
- 自行安装 .net 5 runtime.
- 从[示例项目](https://github.com/focus-creative-games/luban_examples/tree/main/Tools/Luban.Server)拷贝 Luban.Server**可跨平台即使在linux、mac平台也不需要重新编译**
- 在Luban.Server目录下运行 dotnet Luban.Server.dll (提示Win平台可以直接运行 Luban.Server.exe)
#### 安装 luban-client
- 基于 docker (只推荐与jenkins之类devops工具配合使用因为docker容器启动会增加一定的延迟)
docker run --rm -v $PWD/.cache.meta:/bin/.cache.meta focuscreativegames/luban-client <参数>
提醒! .cache.meta这个文件用于保存本地生成或者提交到远程的文件md5缓存**强烈推荐** 添加-v $PWD/.cache.meta:/bin/.cache.meta 映射不然每次重新计算所有涉及文件的md5,这可能在项目后期会造成多达几秒的延迟。
- 基于 .net 5 runtime 推荐win平台使用可跨平台不需要重新编译
- 自行安装 .net 5 runtime.
- 从[示例项目](https://github.com/focus-creative-games/luban_examples/tree/main/Tools/Luban.Client)拷贝 Luban.Client**可跨平台即使在linux、mac平台也不需要重新编译**
### 方法2 Client与Server一体模式
Client与Server在同个进程内运行不需要单独部属Luban.Server。
将运行脚本中%LUBAN_CLIENT%变量由 Luban.Client/Luban.Client 改为 Luban.ClientServer/Luban.ClientServer同时删除 -h (--host ) 选项及其参数(如果指定了-h选项则不启动内嵌Luban.Server使用云生成
Luban.ClientServer是Luban.Client的功能超集可以完全替代Luban.Client。
-----
## 使用
### luban-server 使用介绍
命令行使用
dotnet Luban.Server.dll [-p <port>] [-l <log level>]
参数介绍:
-p <port> 可选参数。 监听端口 <port>默认为8899。
-l <log level> 可选参数。 日志级别。默认为INFO。 有效值有: TRACE,DEBUG,INFO,WARN,ERROR,FATAL,OFF
## luban-client 使用介绍
命令行使用
dotnet Luban.Client.dll [-h <host>] [-p <port>] [-l <log level>] [-c <cache meta file>] [-w <watch dirs>] [-h ] -j cfg -- <job options>
参数介绍:
-h,--host <host> 可选参数。 luban-server的地址。默认为 127.0.0.1
-p,--port <port> 可选参数。 luban-server的端口。默认为 8899
-j,--job <job> 必选参数。 生成类型。目前支持的生成类型有: cfg,proto,db。 生成配置请取cfg。
-l,--loglevel <log level> 可选参数。 日志级别。默认为INFO。有效值有: TRACE,DEBUG,INFO,WARN,ERROR,FATAL,OFF
-c,--cachemetafile <meta file> 可选参数。 meta缓存文件名。 默认为 .cache.meta
-w,--watch <dir1;dir2...> 可选参数。 监测目录或者目录列表,以逗号';'分隔。当开启此选项后,生成结束后不会退出程序,而是进入自动生成模式。监听到目标目录发生变化后,自动重新运行生成。省去改动后手动运行生成脚本的麻烦。
--generateonly 可选参数。 仅生成。不从服务器下载生成结果。可以用于检查服务器是否能成功生成。
-h,--help 可选参数。显示帮助信息
-- <job options> 必选参数。 从此参数起,便是 不同job的特有选项
----
cfg的<job options>介绍:
-d,--define_file <root file> 必选参数。 根定义文件名。
--input_data_dir <input data dir> 必选参数。 配置数据文件根目录。
-c,--output_code_dir <output code dir> 可选参数。 生成代码文件的目录。
-s,--service 必选参数。生成分组目标。一般来说会定义client,server,editor等好几个目标不同目标的生成内容不同。
--gen_types <type1,type2,,,> 必选参数。生成任务类型。既可以是生成代码也可以是生成数据或者其他。目前支持的有 code_cs_bin,code_cs_json,code_cs_unity_json,code_lua_bin,code_java_bin,code_go_bin,code_go_json,code_cpp_bin,code_python27_json,code_python3_jsoncode_typescript_bin,code_typescript_json,data_bin,data_lua,data_json,data_json_monolithic,data_template
--output_data_dir <output data dir> 可选参数。 导出的数据文件的目录。
--validate_root_dir <path validate root dir>. 可选参数。 配置path检查的根目录。
--export_test_data 可选参数。 是否导出测试数据。默认为false
--template_name <template name> 可选参数。数据模板的名称不包含后缀当gen_types包含 data_template时必须指定。
--data_file_extension <output data file extension> 可选参数。 导出数据文件的后缀。默认按照生成类型自动选择。
-t,--l10n_timezone <timezone> 可选参数。 指定所在时区。影响datetime类型转换为utc时间。 默认为中国北京时间。
--input_l10n_text_files <file1,file2..> 可选参数。 本地化的文本映射表。可以有多个。
--l10n_text_field_name <field name> 可选参数。 文本映射表中目标映射列的列名默认为text
--output_l10n_not_translated_text_file <file> 可选参数。 未被本地化映射的text key和value的输出文件。不提供该参数则不生成
--patch <patch name> 可选参数。当前需要生成的分支名称。
--patch_input_data_dir <patch data root dir> 可选参数。分支数据的根目录。
## 示例
假设
luban.server 运行在本机端口为8899
luban.client的位置在 d:\tools\Luban.Client
配置定义在 d:\raw_config\defines
配置定义的根定义文件为 d:\raw_config\defines\__root__.xml
配置数据在 d:\raw_configs\datas
client项目为unity项目位置在 d:\client
你期望client生成的代码在 d:\client\Assets\Gen 目录
你期望client生成的数据在 d:\client\Assets\StreamingAssets\GameData 目录
你们服务器项目为 dotnet c#项目位置在d:\server
你期望server生成的代码在 d:\server\src\Gen
你期望server生成的数据在 d:\server\GameData
案例1
你要为客户端生成代码和数据。
你期望使用bin格式的导出数据类型
你为客户端选择的service分组为 client
当前在开发期,你期望导出数据中包含测试数据
则win下命令为
dotnet d:\tools\Luban.Client\Luban.Client.dll ^
-h 127.0.0.1 ^
-p 8899 ^
-j cfg ^
-- ^
--define_file d:\raw_config\defines\__root__.xml ^
--input_data_dir d:\raw_configs\datas ^
--output_code_dir d:\client\Assets\Gen ^
--output_data_dir d:\client\Assets\StreamingAssets\GameData ^
--gen_types code_cs_bin,data_bin ^
--service client ^
--export_test_data
linux bash命令同理。
案例2
你要为客户端生成代码和数据。
你期望使用json格式导出数据类型。
你不期望导出数据中包含测试数据
则win下命令为:
dotnet d:\tools\Luban.Client\Luban.Client.dll ^
-h 127.0.0.1 ^
-p 8899 ^
-j cfg ^
-- ^
--define_file d:\raw_config\defines\__root__.xml ^
--input_data_dir d:\raw_configs\datas ^
--output_code_dir d:\client\Assets\Gen ^
--output_data_dir d:\client\Assets\StreamingAssets\GameData ^
--gen_types code_cs_unity_json,data_json ^
--service client
案例3
你要为服务器生成代码和数据。
你期望使用json导出数据格式。
你期望包含测试数据。
你为服务器选择的service为server
则 win下命令为
dotnet d:\tools\Luban.Client\Luban.Client.dll ^
-h 127.0.0.1 ^
-p 8899 ^
-j cfg ^
-- ^
--define_file d:\raw_config\defines\__root__.xml ^
--input_data_dir d:\raw_configs\datas ^
--output_code_dir d:\server\src\Gen ^
--output_data_dir d:\server\GameData ^
--gen_types code_cs_json,data_json ^
--service server ^
--export_test_data
案例4
luban-server 被你部属在 192.168.1.10这台机器上端口为1111。其他的如案例3。
则 win下的生成命令为
dotnet d:\tools\Luban.Client\Luban.Client.dll ^
-h 192.168.1.10 ^
-p 1111 ^
-j cfg ^
-- ^
--define_file d:\raw_config\defines\__root__.xml ^
--input_data_dir d:\raw_configs\datas ^
--output_code_dir d:\server\src\Gen ^
--output_data_dir d:\server\GameData ^
--gen_types code_cs_json,data_json ^
--service server ^
--export_test_data
案例5
你要为服务器生成代码和数据。同时让luban.client执行生成后不退出进入监控状态只要配置定义或者数据有变化就自动生成代码和数据。
你期望使用json导出数据格式。
你期望包含测试数据。
你为服务器选择的service为server
则 win下命令为
dotnet d:\tools\Luban.Client\Luban.Client.dll ^
-h 127.0.0.1 ^
-p 8899 ^
-j cfg ^
-w d:\raw_config\defines;d:\raw_configs\datas
-- ^
--define_file d:\raw_config\defines\__root__.xml ^
--input_data_dir d:\raw_configs\datas ^
--output_code_dir d:\server\src\Gen ^
--output_data_dir d:\server\GameData ^
--gen_types code_cs_json,data_json ^
--service server ^
--export_test_data

View File

@ -1,2 +0,0 @@
# TODO 待补充

View File

@ -1,92 +0,0 @@
# 模板
支持代码模板和数据模板,可以灵活定制生成的代码和数据。
## 代码模板
使用scriban模板文件定制导出数据格式。例如生成cs语言bin数据格式的cfg.Tables类的模板如下。
```
using Bright.Serialization;
{{
name = x.name
namespace = x.namespace
tables = x.tables
}}
namespace {{namespace}}
{
public sealed class {{name}}
{
{{~for table in tables ~}}
{{~if table.comment != '' ~}}
/// <summary>
/// {{table.comment}}
/// </summary>
{{~end~}}
public {{table.full_name}} {{table.name}} {get; }
{{~end~}}
public {{name}}(System.Func<string, ByteBuf> loader)
{
var tables = new System.Collections.Generic.Dictionary<string, object>();
{{~for table in tables ~}}
{{table.name}} = new {{table.full_name}}(loader("{{table.output_data_file}}"));
tables.Add("{{table.full_name}}", {{table.name}});
{{~end~}}
{{~for table in tables ~}}
{{table.name}}.Resolve(tables);
{{~end~}}
}
public void TranslateText(System.Func<string, string, string> translator)
{
{{~for table in tables ~}}
{{table.name}}.TranslateText(translator);
{{~end~}}
}
}
}
```
### 数据模板
使用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
```

View File

@ -1,14 +0,0 @@
## road map 1
* 补充单元测试
* 新增静态本地化支持
* 新增动态本地化支持
* 新增生成用于unreal编辑器的 加载及保存json配置的cpp代码
* 新增生成用于unity 编辑器的 加载及保存json配置的c#代码
## road map 2
* 新增 unity 内嵌编辑器
* 新增 unreal 内嵌编辑器

View File

@ -1,26 +0,0 @@
[//]: # (Author: bug)
[//]: # (Date: 2020-10-20 20:22:07)
## Python 示例
```Python
```
## TS 示例
``` typescript
```
## C# 示例
``` csharp
```
## C++ 示例
``` c_cpp
```
## Java 示例
``` java
```
## Go 示例
``` go
```

View File

@ -1,106 +0,0 @@
# 安装与执行
## 安装
1. dotnet sdk 5.0
1. 下载luban_examples项目
下载项目 [luban_examples](https://github.com/focus-creative-games/luban_examples)。
项目中包含测试配置、最新的luban_client&luban_server工作以及大量的示例项目。为方便起见后续提及到的文件默认都指这个项目中的文件。
## 创建游戏配置
1. 从示例项目拷贝[MiniDesignerConfigsTemplate](https://github.com/focus-creative-games/luban_examples/MiniDesignerConfigsTemplate) 到一个合适的目录,假设为 **MyConfigs**
2. 添加物品表 excel 文件
在 MyConfigs/Datas 目录下创建一个 “物品表.xlsx” 文件。
![如图](images/install/install_03.png)
文件内容如下
|##|id|name|desc|count|
|-|-|-|-|-|
|##type|int|string|string|int|
|##|id|名字|描述|个数|
||1001|item1| desc1| 10|
||1002|item2| desc2| 10|
||1003|item3| desc3| 10|
||1004|item4| desc4| 10|
- 第 1 行是 主字段行,包含表字段定义。单元格 A1 必须是 ##。表示这是一个有效数据表。
- 第 2 行是类型行第1个单元格必须为 ##type。
- 第 3 行是注释行。 以##开头。 可以有0-N个注释行而且可以出现在任何位置
- 第 4 行起是数据行。
3. 在 Datas 目录下的__tables__.xlsx添加表声明。如下图
|##|full_name|value_type|define_from_excel|input|index|mode|group|comment|patch_input|tags|output|
|-|-|-|-|-|-|-|-|-|-|-|-|
||demo.TbItem|Item|true|物品表.xlsx||||||||
4. 至此,物品表的创建工作大功告成!
## 生成代码和数据以及在程序中使用
假设是Unity项目使用json导出格式。 示例参考项目为 [Csharp_Unity_Json](https://github.com/focus-creative-games/luban_examples/Projects/Csharp_Unity_json)。其他语言或者导出类型的组合,请参考 [luban_examples](https://github.com/focus-creative-games/luban_examples)
1. 项目准备。
拷贝示例项目中 Assets\LubanLib 目录到你的Unity项目中可以自由组织位置此时尝试编译项目理论上应该能成功编译。
2. 运行生成命令可以参考示例项目的gen_code_json.bat
```bat
dotnet <Luban.ClientServer.dll>
-j cfg ^
-- ^
--define_file <__root__.xml > ^
--input_data_dir <配置数据根目录(Datas)的路径> ^
--output_code_dir <生成的代码文件的路径> ^
--output_data_dir <导出的数据文件的路径> ^
--service all ^
--gen_types "code_cs_json,data_json"
```
其中
- <Luban.ClientServer.dll> 指向 Tools/Luban.ClientServer/Luban.ClientServer.dll
- --define_file 参数为 <MyConfigs>/Defines/\_\_root\_\_.xml 的路径
- --input_data_dir 参数为 <MyConfigs>/Datas 的路径
- --output_code_dir 参数为生成的代码文件存放的路径。 建议建议指向 unity的 Assets 目录下的某级子目录
- --output_data_dir 参数为生成的数据文件的存放路径。
详细的命令文档请看 [install_manual](./luban_install_manual.md)。
如果一切正常,会产生一系列日志,最终一行是 == succ == 。
类似这样
![生成结果](images/install/install_07.png)
如果一切顺利。生成的代码文件会在 output_code_dir 参数指定的 目录中,生成的配置数据会在 output_data_dir 参数指定的目录中。把 output_code_dir 加入到 项目中,编译。此时应该能编译成功。
3. 加载配置
只需一行代码既可完成所有配置表的加载工具
```c#
var tables = new cfg.Tables(file => JSON.Parse(File.ReadAllText(gameConfDir + "/" + file + ".json")));
```
4. 使用加载后的配置表
cfg.Tables 里包含所有配置表的一个实例字段。加载完 cfg.Tables 后,只需要用 tables.<表名> 就能获得那个表实例接着可以做各种操作。例如我们要获取id = 10000 的那个道具。代码如下
```c#
cfg.item.Item itemInfo = tables.TbItem.Get(10000);
Console.WriteLine("id:{0} name:{1} desc:{2}", itemInfo.Id, itemInfo.Name, itemInfo.Desc);
```
可能你会注意到item.xml 里定义 Item 时,字段名 id,name,desc的首字母被大写了。这是因为工具会根据输出的语言自动作相应代码风格的字段名转换也即 boo_bar 会被转换为 BooBar 这样的名字。这也是为什么推荐 配置字段定义时统一使用 xx_yy_zz 的风格。
5. 至此完成 配置加载与使用!

View File

@ -1,137 +0,0 @@
[//]: # (Author: bug)
[//]: # (Date: 2020-11-08 18:03:58)
## 特性
### 完备的类型系统
* 基础内置类型
- bool,byte,short,fshort,int,fint,long,flong,float,double,string,text,bytes
- vector2, vector3,vector4
- datetime
* 可空类型
- bool?,byte?,short?,fshort?,int?,fint?,long?,flong?,float?,double?
- vector2?,vector3?,vector4?
- datetime?
- <枚举>?
- < bean>?
- <多态bean>?
* 自定义枚举 enum 及相应可空类型
* 自定义常量 const
* 自定义结构 bean
* 多态bean
支持定义无限层次的OOP类型继承体系(比如父类Shape子类Circle,Rectangle),在表达复杂配置时极为简洁,对程序和策划都比较友好。
* 支持容器类型 array。 value 可以为内置类型,也可以为自定义类型
* 支持容器类型 list。 value 可以为内置类型,也可以为自定义类型
* 支持容器类型 set。 value 只能为内置类型或者enum类型不支持 bean 类型
* 支持容器类型 map。 key 只能为内置类型或者enum类型不支持 bean 类型。 value 可以为内置类型,也可以为自定义类型
### 支持增强的excel格式
* 用 true,false表示 bool变量。
* 用枚举名及别名表示枚举常量。比如用 白绿红之类表示品质而不是1,2,3这样的magic number
* 支持整数的常量替换。比如说 升级丹道具id 为 1122所有填升级丹id的地方可以填 升级丹 来表示。减少填写错误
* 支持可空变量. 用 null 表示空数据.
* 支持 datetime 数据类型. 时间格式标准为以下几种最终被转化为utc时间方便程序处理
- yyyy-MM-dd HH:mm:ss
- yyyy-MM-dd HH:mm
- yyyy-MM-dd HH
- yyyy-MM-dd
* 支持用sep拆分单元格。在一个单元格里填写多个数据。
* 支持多行数据。例如,章节配置里有一个复杂小节列表字段。支持多行填写这个列表数据。
* 支持多级标题头,方便对应一些比较深的数据。比如 a.b.c 这种。
* 支持多态别名,可以方便填写多态数据。比如说 Circle,5 或者 Rectangle,3,4
* **支持在excel里比较简洁填写出任意复杂的配置**。
- 支持结构列表。 比如 list,Equip (包含 int id, string name, float attr) 这样一个复杂结构列表数据,可以填成 1,abasfa,0.5|2,xxxyy;0.9。
- 支持多态结构。 比如 cfg.Shape 是一个多态类型,包含 Cirecle(float radius)和Rectagnle(float width, float size)。 可以填成 圆,5 或者 长方形,3,5。
- 支持无限层次的复杂结构的组合
- 比如 list,Convex(int id, Vector3[] vertexs) 是一个多边形列表, Convext自身包含一个顶点列表可以配置成 1_1.2,2.3,3.4_3.1,3.2,3.3|2_2.2,2.3.3.3 。
- 比如 list,Shape 是一个形状列表。 可以这样配置 Circle,10;Rectange,5,6;Circle,4
### 多种原始文件格式支持
一个复杂项目中,总有一部分数据(10-20%)不适合excel编辑比如技能、AI、副本等等这些数据一般通过专用编辑器来编辑和导出。遇到的问题是这种配置数据是无法与excel数据统一处理的造成游戏内有多种配置数据加载方案程序需要花费很多时间去处理这些数据的加载问题。另外这些复杂数据无法使用数据检验和分组导出以及本地化等等excel导表工具的机制。Luban能够处理excel族、xml、json、lua、目录等多种数据源统一导出数据和生成代码所有数据源都能使用数据检验、分组导出等等机制程序彻底从复杂配置处理中解脱出来。
* 支持excel族文件。 csv 及 xls,xlsx等格式
* 支持从指定excel里的某个单元薄读入。
* 支持json。 每个json文件当作一个记录读入
* 支持lua。 每个lua文件当作一个记录读入
* 支持xml。 每个xml文件当作一个记录读入
* 支持目录。 递归目录下的所有文件每个文件当作一个记录读入。允许不同类型的文件混合比如目录下可以同时有json,lua,xml,excel之类的格式。
* 每个表允许指定多个数据源,可以使用以上所有的组合。
- 多对一。比如可以在一个excel里用不同页签配置不同的表。比如 装备升级表和进阶表都在 装备表.xlsx中
- 一对多。比如任务表可以来 任务1.xlsx任务2.xlsx 等等多个表。
- 多对多。还可以是以上组合,不过实际中很少见)
### 多种导出数据格式支持
**导出格式与原始数据解耦**。无论源数据是 excel、lua、xml、json 或者它们的混合, 最终都被以**统一的格式**导出,极大简化了生成代码的复杂性。 目前支持以下几种导出格式:
* binary格式。与pb格式类似。所占空间最小加载最快。
* json 格式。
* lua 格式。
* 扩展其他格式也很容易。像前几种数据格式导出只用200行代码
### 支持表与字段级别分组
支持自定义分组类型。既支持按分组选择性导出一部分表,也支持选择性导出表中的一部分字段。比如为前后端分别导出他们所用的数据。
### 支持数据标签
支持 是、否、test 三种标签。可以为每行数据加标签。比如标签为"否"表示这行数据不被导出。 如果为 "test",则只在测试导出情况下才导出。比如内部开发时会配置一些测试数据,但对外发布时不希望导出它们的情形。
### 强大的数据校验能力
* 完整的数据内建约束检查
* ref 检查。检查表引用合法性。比如 Monster表中的dropId必须是合法的 TbDrop表的key.
* path 检查。检查资源引用合法性。比如 Monster表的icon必须是合法的图标资源。对于防止策划填错极其有用不再需要运行时才发现资源配置错误了。
* range 检查。检查数值范围。
* 扩展的自定义检查。使用自定义代码进行高级检查。提交配置前就能完成本地检查,避免运行时才发现错误,极大减少迭代成本。
### 多种数据表模式
* one 格式,即单例表模式
* map 格式即普通key-value表模式。 任何符合set 的value要求的类型都可以做key
### 本地化支持
* 支持**本地化时间**。 配置中的 datetime会根据指定的timezone及localtime转化为正确的utc时间方便程序处理
* 支持**静态本地化**。 配置中的text类型在导出时已经转化为正确的本地化后的字符串
* 支持**动态本地化**。 配置中的text类型能运行时全部切换为某种本地化后的字符串
### 代码编辑器支持
支持 emmylua anntations。生成的lua包含符合emmylua 格式anntations信息。配合emmylua有良好的配置代码提示能力。
### 资源导出支持
支持 res 资源标记。可以一键导出配置中引用的所有资源列表(icon,ui,assetbundle等等)
### 代码模块化
生成模块化的代码。比如
- c# cfg.item.ItemInfo
- c++ cfg::item::ItemInfo
- lua item.ItemInfo
- go item_ItemInfo
### 生成极快,大型项目也能秒级导出
使用 client/server模式利用服务器强大的硬件(大内存+多线程)同时配合缓存机制如果数据和定义未修改直接返回之前生成过的结果即使到项目中后期数据规模比较大也能1秒传统在10秒以上左右生成所有数据并且完成数据校验。考虑策划和程序经常使用生成工具积少成多也能节省大量研发时间。
### 数据模块化
策划可以方便地按需求自己组织数据目录和结构,不影响逻辑表。
### 支持主流的游戏开发语言
- c++ (11+)
- c# (.net framework 2+. dotnet core 2+)
- java (1.6+)
- go (1.10+)
- lua (5.1+)
- js 和 typescript (3.0+)
- python (2.7+ 及 3.0+)
### 支持主流引擎和平台
- unity + c#
- unity + tolua,xlua
- unity + ILRuntime
- unreal + c++
- unreal + unlua
- unreal + sluaunreal
- unreal + puerts
- cocos2d-x + lua
- cocos2d-x + js
- 微信小程序平台
- 其他家基于js的小程序平台
- 其他所有支持lua的引擎和平台
- 其他所有支持js的引擎和平台

View File

@ -1,2 +1,6 @@
**/bin/
**/obj/
/lubanAssistant/
/packages/
/TestResults/
LubanTools.sln

View File

@ -1,13 +0,0 @@
[*.cs]
# CA1303: 请不要将文本作为本地化参数传递
dotnet_diagnostic.CA1303.severity = none
# CA1305: 指定 IFormatProvider
dotnet_diagnostic.CA1305.severity = none
# CA1307: 指定 StringComparison
dotnet_diagnostic.CA1307.severity = none
# CA1031: 不捕获常规异常类型
dotnet_diagnostic.CA1031.severity = none

View File

@ -2,7 +2,11 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<StartupObject>Luban.Client.Program</StartupObject>
<PackageProjectUrl>https://github.com/focus-creative-games/luban</PackageProjectUrl>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<ItemGroup>
@ -12,7 +16,14 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.8.0" />
<None Include="..\..\README.md">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="YamlDotNet" Version="11.2.1" />
</ItemGroup>
<ItemGroup>

View File

@ -2,8 +2,8 @@
"profiles": {
"Luban.Client": {
"commandName": "Project",
"commandLineArgs": "-h %LUBAN_SERVER_IP% -j cfg -w ..\\DesignerConfigs\\Datas -- -d ..\\DesignerConfigs\\Defines\\__root__.xml --input_data_dir ..\\DesignerConfigs\\Datas --output_code_dir TsScripts/src/Gen/Cfg --output_data_dir ConfigData --gen_types code_typescript_bin,data_bin -s client --export_test_data --validate_root_dir Assets ",
"workingDirectory": "C:\\workspace\\unity-world\\Client"
"commandLineArgs": "-h %LUBAN_SERVER_IP% -j cfg -- -d ..\\..\\DesignerConfigs\\Defines\\__root__.xml --input_data_dir ..\\..\\DesignerConfigs\\Datas --output_code_dir Assets/Gen --output_data_dir ..\\GenerateDatas\\json --gen_types code_cs_unity_json,data_json -s all --validate_root_dir .",
"workingDirectory": "D:\\workspace\\luban_examples\\Projects\\Csharp_Unity_json"
},
"Luban.Client-db": {
"commandName": "Project",

View File

@ -1,20 +1,27 @@
FROM mcr.microsoft.com/dotnet/sdk:5.0 as build
FROM mcr.microsoft.com/dotnet/sdk:6.0 as build
WORKDIR /app/Luban.Common
COPY Luban.Common/*.csproj ./
COPY Luban.Common/Source ./Source
COPY Luban.Common/.editorconfig .
COPY nuget.config ./nuget.config
WORKDIR /app/Luban.Client
COPY Luban.Client/Luban.Client.csproj ./
COPY Luban.Client/.editorconfig .
COPY Luban.Client/Source ./Source
COPY nuget.config ./nuget.config
RUN dotnet restore
WORKDIR /app/Luban.Common
COPY Luban.Common/Source ./Source
WORKDIR /app/Luban.Client
COPY Luban.Client/Source ./Source
RUN dotnet publish -c Release -o out
FROM mcr.microsoft.com/dotnet/runtime:5.0 AS runtime
FROM mcr.microsoft.com/dotnet/runtime:6.0 AS runtime
WORKDIR /app
COPY --from=build /app/Luban.Client/out ./

View File

@ -8,11 +8,14 @@ using Luban.Client.Common.Utils;
using Luban.Common.Protos;
using Luban.Common.Utils;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using YamlDotNet.RepresentationModel;
namespace Luban.Client.Common.Net
{
@ -45,7 +48,7 @@ namespace Luban.Client.Common.Net
EventLoop = new EventLoop(null),
InitHandler = ch =>
{
ch.Pipeline.AddLast(new ProtocolFrameCodec(20_000_000, new RecycleByteBufPool(100, 10), new DefaultProtocolAllocator(factories)));
ch.Pipeline.AddLast(new ProtocolFrameCodec(100 * 1024 * 1024, new RecycleByteBufPool(100, 10), new DefaultProtocolAllocator(factories)));
ch.Pipeline.AddLast(Ins);
}
};
@ -99,7 +102,7 @@ namespace Luban.Client.Common.Net
private async Task OnGetImportFileOrDirectoryAsync(GetImportFileOrDirectory rpc)
{
long t1 = TimeUtil.NowMillis;
long t1 = Bright.Time.TimeUtil.NowMillis;
var file = rpc.Arg.FileOrDirName;
var suffixes = rpc.Arg.InclusiveSuffixs;
var re = new GetImportFileOrDirectoryRes()
@ -139,14 +142,14 @@ namespace Luban.Client.Common.Net
s_logger.Error(e);
}
s_logger.Trace(" GetImportFileOrDirectory file:{file} err:{err} cost:{time}", file, re.Err, TimeUtil.NowMillis - t1);
s_logger.Trace(" GetImportFileOrDirectory file:{file} err:{err} cost:{time}", file, re.Err, Bright.Time.TimeUtil.NowMillis - t1);
Session.ReplyRpc<GetImportFileOrDirectory, GetImportFileOrDirectoryArg, GetImportFileOrDirectoryRes>(rpc, re);
}
private async Task OnGetInputFileAsync(GetInputFile rpc)
{
long t1 = TimeUtil.NowMillis;
long t1 = Bright.Time.TimeUtil.NowMillis;
var res = new GetInputFileRes() { Err = Luban.Common.EErrorCode.OK };
try
{
@ -158,19 +161,89 @@ namespace Luban.Client.Common.Net
{
res.Err = Luban.Common.EErrorCode.READ_FILE_FAIL;
}
s_logger.Info(" get input file:{file} err:{err} cost:{time}", rpc.Arg.File, res.Err, TimeUtil.NowMillis - t1);
s_logger.Info(" get input file:{file} err:{err} cost:{time}", rpc.Arg.File, res.Err, Bright.Time.TimeUtil.NowMillis - t1);
Session.ReplyRpc<GetInputFile, GetInputFileArg, GetInputFileRes>(rpc, res);
}
private readonly Regex _subResPattern = new Regex(@"(.+)\[(.+)\]$");
private readonly ConcurrentDictionary<string, YamlDocument> _cacheYamlDocs = new();
private YamlDocument GetCacheYamlDoc(string mainResFileName)
{
return _cacheYamlDocs.GetOrAdd(mainResFileName, (file) =>
{
var yamlStream = new YamlStream();
yamlStream.Load(new StreamReader(new FileStream(mainResFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)));
return yamlStream.Documents[0];
});
}
private bool CheckSubResourceExists(string mainResFileName, string subResName)
{
s_logger.Debug("check resources main:{} sub:{}", mainResFileName, subResName);
if (!FileUtil.IsFileExistsSenseCase(mainResFileName))
{
return false;
}
try
{
var yamlNode = (YamlMappingNode) GetCacheYamlDoc(mainResFileName).RootNode;
var yamlSubResName = new YamlScalarNode(subResName);
foreach (var (resType, node) in yamlNode)
{
switch(resType.ToString())
{
case "SpriteAtlas":
{
var mnode = (YamlMappingNode)node;
var r = (YamlSequenceNode) mnode[new YamlScalarNode("m_PackedSpriteNamesToIndex")];
if (r == null)
{
return false;
}
return r.Contains(yamlSubResName);
}
}
}
return false;
}
catch (Exception ex)
{
s_logger.Error(ex);
return false;
}
}
private void Process(QueryFilesExists p)
{
var root = p.Arg.Root;
var files = p.Arg.Files;
var re = new QueryFilesExistsRes() { Exists = new List<bool>(files.Count) };
var tasks = new List<Task<bool>>();
foreach (var f in files)
{
re.Exists.Add(File.Exists(Path.Combine(root, f)));
var match = _subResPattern.Match(f);
if (match.Success)
{
var groups = match.Groups;
tasks.Add(Task.Run(() => CheckSubResourceExists(Path.Join(root, groups[1].Value), groups[2].Value)));
}
else
{
tasks.Add(Task.Run(() => FileUtil.IsFileExistsSenseCase(Path.Combine(root, f))));
}
}
Task.WhenAll(tasks);
foreach (var task in tasks)
{
re.Exists.Add(task.Result);
}
Session.ReplyRpc<QueryFilesExists, QueryFilesExistsArg, QueryFilesExistsRes>(p, re);
}

View File

@ -269,7 +269,7 @@ Options:
}
else
{
s_logger.Error("GenJob fail. err:{err} msg:{msg}", res.ErrCode, res.ErrMsg);
s_logger.Error("GenJob fail. msg:{msg}", res.ErrMsg);
if (!string.IsNullOrEmpty(res.StackTrace))
{
s_logger.Debug("StackTrace: {}", res.StackTrace);

View File

@ -21,6 +21,7 @@ namespace Luban.Client.Common.Utils
public void AddOutputDir(string dir)
{
dir = Path.TrimEndingDirectorySeparator(dir);
_outputDirs.Add(dir);
}
@ -90,7 +91,7 @@ namespace Luban.Client.Common.Utils
else
{
s_logger.Info("[remove] dir: {dir}", subDir);
Directory.Delete(subDir);
FileUtil.DeleteDirectoryRecursive(subDir);
}
}
}

View File

@ -49,7 +49,7 @@ namespace Luban.Client.Utils
}
}
private readonly static List<string> _filterSuffixs = new List<string>
private static readonly List<string> _filterSuffixs = new List<string>
{
".xlsx",
".csv",

View File

@ -2,9 +2,20 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<StartupObject>Luban.ClientServer.Program</StartupObject>
<PackageProjectUrl>https://github.com/focus-creative-games/luban</PackageProjectUrl>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<ItemGroup>
<None Include="..\..\README.md">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Luban.Client\Luban.Client.csproj" />
<ProjectReference Include="..\Luban.Server\Luban.Server.csproj" />

View File

@ -4,10 +4,13 @@ using Luban.Client.Common.Utils;
using Luban.Client.Utils;
using Luban.Common.Protos;
using Luban.Common.Utils;
using Luban.Job.Cfg.Cache;
using Luban.Job.Common.Tpl;
using Luban.Job.Common.Utils;
using Luban.Server;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -80,12 +83,14 @@ Options:
// 打个补丁。好多人忘了设置 LUBAN_SERVER_IP 环境变量,导致启动时出问题
if (args[i + 1].StartsWith("-"))
{
Console.WriteLine("[WARN] --host (or -h) <LUBAN_SERVER_IP> argument is missing, use 127.0.0.1 as default. do you forget to set LUBAN_SERVER_IP env variable?");
ops.Host = "127.0.0.1";
//Console.WriteLine("[WARN] --host (or -h) <LUBAN_SERVER_IP> 参数丢失, use 127.0.0.1 as default. do you forget to set LUBAN_SERVER_IP env variable?");
//ops.Host = "127.0.0.1";
Console.WriteLine("[WARN] 对于Luban.ClientServer参数 {0} 没有意义,忽略。", arg);
}
else
{
ops.Host = args[++i];
string argv = args[++i];
Console.WriteLine("[WARN] 对于Luban.ClientServer参数 {0} {1} 没有意义,忽略。", arg, argv);
}
break;
}
@ -133,7 +138,15 @@ Options:
case "-t":
case "--template_search_path":
{
ops.TemplateSearchPath = args[++i];
var dirName = args[++i];
if (Directory.Exists(dirName))
{
ops.TemplateSearchPath = dirName;
}
else
{
Console.WriteLine("[WARN] 对于Luban.ClientServer参数 {0} {1} 路径不存在,忽略。", arg, dirName);
}
break;
}
case "--":
@ -163,11 +176,13 @@ Options:
private static void StartServer(AllCommandLineOptions options)
{
FileRecordCacheManager.Ins.Init(true);
StringTemplateManager.Ins.Init(true);
if (!string.IsNullOrEmpty(options.TemplateSearchPath))
{
StringTemplateUtil.AddTemplateSearchPath(options.TemplateSearchPath);
StringTemplateManager.Ins.AddTemplateSearchPath(options.TemplateSearchPath);
}
StringTemplateUtil.AddTemplateSearchPath(FileUtil.GetPathRelateApplicationDirectory("Templates"));
StringTemplateManager.Ins.AddTemplateSearchPath(FileUtil.GetPathRelateApplicationDirectory("Templates"));
GenServer.Ins.Start(true, options.Port, ProtocolStub.Factories);
@ -213,7 +228,7 @@ Options:
LogUtil.InitSimpleNLogConfigure(NLog.LogLevel.FromString(options.LogLevel));
s_logger = NLog.LogManager.GetCurrentClassLogger();
TimeZoneUtil.InitDefaultTimeZone("");
int processorCount = System.Environment.ProcessorCount;
ThreadPool.SetMinThreads(Math.Max(4, processorCount), 5);
@ -332,4 +347,4 @@ Options:
return 0;
}
}
}
}

View File

@ -0,0 +1,63 @@
FROM mcr.microsoft.com/dotnet/sdk:6.0 as build
WORKDIR /app/Luban.Common
COPY Luban.Common/*.csproj ./
COPY Luban.Common/.editorconfig ./
WORKDIR /app/Luban.ClientServer.Common
COPY Luban.ClientServer.Common/*.csproj ./
COPY nuget.config ./nuget.config
WORKDIR /app/Luban.Job.Common
COPY Luban.Job.Common/*.csproj ./
COPY nuget.config ./nuget.config
WORKDIR /app/Luban.Job.Cfg
COPY Luban.Job.Cfg/*.csproj ./
COPY nuget.config ./nuget.config
WORKDIR /app/Luban.Job.Proto
COPY Luban.Job.Proto/*.csproj ./
COPY nuget.config ./nuget.config
WORKDIR /app/Luban.Job.Db
COPY Luban.Job.Db/*.csproj ./
COPY nuget.config ./nuget.config
WORKDIR /app/Luban.ClientServer
COPY Luban.ClientServer/Luban.ClientServer.csproj ./
COPY Luban.ClientServer/.editorconfig .
COPY nuget.config ./nuget.config
RUN dotnet restore
WORKDIR /app/Luban.Common
COPY Luban.Common/Source ./Source
WORKDIR /app/Luban.ClientServer.Common
COPY Luban.ClientServer.Common/Source ./Source
WORKDIR /app/Luban.Job.Common
COPY Luban.Job.Common/Source ./Source
WORKDIR /app/Luban.Job.Cfg
COPY Luban.Job.Cfg/Source ./Source
WORKDIR /app/Luban.Job.Proto
COPY Luban.Job.Proto/Source ./Source
WORKDIR /app/Luban.Job.Db
COPY Luban.Job.Db/Source ./Source
WORKDIR /app/Luban.ClientServer
COPY Luban.ClientServer/Source ./Source
COPY Luban.Server/Templates ./Templates
RUN dotnet publish -c Release -o out
FROM mcr.microsoft.com/dotnet/runtime:6.0 AS runtime
WORKDIR /app
COPY --from=build /app/Luban.ClientServer/out ./
EXPOSE 8899/tcp
ENTRYPOINT ["/app/Luban.ClientServer", "-p", "8899"]

View File

@ -0,0 +1,2 @@
docker build -t luban-clientserver:latest -f Dockerfile ../..
pause

View File

@ -0,0 +1 @@
docker build -t luban-clientserver:latest -f Dockerfile ../..

View File

@ -0,0 +1,3 @@
docker tag luban-clientserver:latest focuscreativegames/luban-clientserver:latest
docker push focuscreativegames/luban-clientserver:latest
pause

View File

@ -0,0 +1,2 @@
docker tag luban-server:latest focuscreativegames/luban-server:latest
docker push focuscreativegames/luban-server:latest

View File

@ -1,11 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<PackageProjectUrl>https://github.com/focus-creative-games/luban</PackageProjectUrl>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Bright.Net" Version="1.1.0.41" />
<PackageReference Include="CommandLineParser" Version="2.8.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,30 @@
using CommandLine;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Luban.Common.Utils
{
public static class CommandLineUtil
{
public static T ParseOptions<T>(String[] args)
{
var helpWriter = new StringWriter();
var parser = new Parser(ps =>
{
ps.HelpWriter = helpWriter;
});
var result = parser.ParseArguments<T>(args);
if (result.Tag == ParserResultType.NotParsed)
{
Console.Error.WriteLine(helpWriter.ToString());
Environment.Exit(1);
}
return ((Parsed<T>)result).Value;
}
}
}

View File

@ -1,5 +1,7 @@
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
@ -12,7 +14,7 @@ namespace Luban.Common.Utils
public static string GetApplicationDirectory()
{
return Path.GetDirectoryName(System.Reflection.Assembly.GetCallingAssembly().Location);
return AppContext.BaseDirectory;
}
public static string GetPathRelateApplicationDirectory(string relatePath)
@ -23,21 +25,13 @@ namespace Luban.Common.Utils
public static string GetFileName(string path)
{
int index = path.Replace('\\', '/').LastIndexOf('/');
#if !LUBAN_LITE
return index >= 0 ? path[(index + 1)..] : path;
#else
return index >= 0 ? path.Substring(index + 1, path.Length - index - 1) : path;
#endif
}
public static string GetParent(string path)
{
int index = path.Replace('\\', '/').LastIndexOf('/');
#if !LUBAN_LITE
return index >= 0 ? path[..index] : ".";
#else
return index >= 0 ? path.Substring(0, index) : ".";
#endif
}
public static string GetFileNameWithoutExt(string file)
@ -57,6 +51,24 @@ namespace Luban.Common.Utils
return Combine(GetParent(rootFile), file);
}
public static bool IsFileExistsSenseCase(string path)
{
if (!File.Exists(path))
{
return false;
}
if (OperatingSystem.IsWindows())
{
var fileName = Path.GetFileName(path);
var files = Directory.GetFiles(Path.GetDirectoryName(path), fileName, new EnumerationOptions() { MatchCasing = MatchCasing.CaseSensitive });
return files.Length > 0;
}
else
{
return true;
}
}
/// <summary>
/// 忽略以 文件名以 '.' '_' '~' 开头的文件
/// </summary>
@ -70,24 +82,7 @@ namespace Luban.Common.Utils
}
var f = new FileInfo(file);
string fname = f.Name;
#if !LUBAN_LITE
return !fname.StartsWith('.') && !fname.StartsWith('_') && !fname.StartsWith('~');
#else
return !fname.StartsWith(".") && !fname.StartsWith("_") && !fname.StartsWith("~");
#endif
}
[ThreadStatic]
private static MD5 s_cacheMd5;
private static MD5 CacheMd5
{
get
{
var md5 = s_cacheMd5 ??= MD5.Create();
md5.Clear();
return md5;
}
}
public static string CalcMD5(byte[] srcBytes)
@ -116,7 +111,8 @@ namespace Luban.Common.Utils
{
return fullName.EndsWith(".csv", StringComparison.Ordinal)
|| fullName.EndsWith(".xls", StringComparison.Ordinal)
|| fullName.EndsWith(".xlsx", StringComparison.Ordinal);
|| fullName.EndsWith(".xlsx", StringComparison.Ordinal)
|| fullName.EndsWith(".xlsm", StringComparison.Ordinal);
}
public static (string, string) SplitFileAndSheetName(string url)
@ -129,7 +125,6 @@ namespace Luban.Common.Utils
else
{
int lastPathSep = url.LastIndexOf('/', sheetSepIndex);
#if !LUBAN_LITE
if (lastPathSep >= 0)
{
return (url[0..(lastPathSep + 1)] + url[(sheetSepIndex + 1)..], url[(lastPathSep + 1)..sheetSepIndex]);
@ -138,16 +133,6 @@ namespace Luban.Common.Utils
{
return (url[(sheetSepIndex + 1)..], url[(lastPathSep + 1)..sheetSepIndex]);
}
#else
if (lastPathSep >= 0)
{
return (url.Substring(0, lastPathSep + 1) + url.Substring(sheetSepIndex + 1), url.Substring(lastPathSep + 1, sheetSepIndex - lastPathSep - 1));
}
else
{
return (url.Substring(sheetSepIndex + 1), url.Substring(lastPathSep + 1, sheetSepIndex - lastPathSep - 1));
}
#endif
}
}
@ -158,19 +143,6 @@ namespace Luban.Common.Utils
Directory.GetParent(outputPath).Create();
if (File.Exists(outputPath))
{
//if (CheckFileNotChange(outputPath, content))
//{
// s_logger.Trace("[not change] {file}", outputPath);
// return;
//}
//else
//{
// s_logger.Info("[override] {file}", outputPath);
// if (File.GetAttributes(outputPath).HasFlag(FileAttributes.ReadOnly))
// {
// File.SetAttributes(outputPath, FileAttributes.Normal);
// }
//}
s_logger.Info("[override] {file}", outputPath);
if (File.GetAttributes(outputPath).HasFlag(FileAttributes.ReadOnly))
{
@ -182,12 +154,7 @@ namespace Luban.Common.Utils
s_logger.Info("[new] {file}", outputPath);
}
#if !LUBAN_LITE
await File.WriteAllBytesAsync(outputPath, content);
#else
await Task.Run(() => File.WriteAllBytes(outputPath, content));
#endif
await WriteAllBytesAsync(outputPath, content);
}
public static async Task<byte[]> ReadAllBytesAsync(string file)
@ -219,5 +186,33 @@ namespace Luban.Common.Utils
}
return bytes;
}
public static async Task WriteAllBytesAsync(string file, byte[] bytes)
{
using var fs = new FileStream(file, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite);
long count = bytes.LongLength;
fs.SetLength(count);
fs.Seek(0, SeekOrigin.Begin);
await fs.WriteAsync(bytes);
}
public static void DeleteDirectoryRecursive(string rootDir)
{
string[] files = Directory.GetFiles(rootDir);
string[] dirs = Directory.GetDirectories(rootDir);
foreach (string file in files)
{
File.SetAttributes(file, FileAttributes.Normal);
File.Delete(file);
}
foreach (string dir in dirs)
{
DeleteDirectoryRecursive(dir);
}
Directory.Delete(rootDir, false);
}
}
}

View File

@ -15,6 +15,7 @@ namespace Luban.Common.Utils
layout = NLog.Layouts.Layout.FromString("${longdate}|${message}${onexception:${newline}${exception:format=tostring}${exception:format=StackTrace}}");
}
logConfig.AddTarget("console", new NLog.Targets.ColoredConsoleTarget() { Layout = layout });
logConfig.AddRule(minConsoleLogLevel, NLog.LogLevel.Off, new NLog.Targets.NullTarget(), "Bright.Net.Channels.*", true);
logConfig.AddRule(minConsoleLogLevel, NLog.LogLevel.Fatal, "console");
NLog.LogManager.Configuration = logConfig;
}

View File

@ -22,13 +22,13 @@ namespace Luban.Common.Utils
public void StartPhase(string name)
{
phaseStack.Push(new Phase() { Name = name, StartTime = TimeUtil.NowMillis });
phaseStack.Push(new Phase() { Name = name, StartTime = Bright.Time.TimeUtil.NowMillis });
}
private Phase EndPhase()
{
var phase = phaseStack.Pop();
phase.EndTime = TimeUtil.NowMillis;
phase.EndTime = Bright.Time.TimeUtil.NowMillis;
phase.ElapseTime = phase.EndTime - phase.StartTime;
return phase;
}

View File

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Luban.Common.Utils
{
public static class TimeZoneUtil
{
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
public static void InitDefaultTimeZone(string timeZoneName)
{
if (timeZoneName?.ToLower() == "local")
{
DefaultTimeZone = TimeZoneInfo.Local;
return;
}
if (timeZoneName?.ToLower() == "utc")
{
DefaultTimeZone = TimeZoneInfo.Utc;
return;
}
if (string.IsNullOrEmpty(timeZoneName))
{
try
{
DefaultTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Asia/Shanghai");
}
catch (Exception)
{
try
{
DefaultTimeZone = TimeZoneInfo.FindSystemTimeZoneById("China Standard Time");
}
catch (Exception ex)
{
s_logger.Error(ex);
throw new ArgumentException("The default timezone ID 'Asia/Shanghai' and 'China Standard Time' was not found on local computer. please set valid timezone manually.");
}
}
}
else
{
DefaultTimeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneName);
}
}
public static TimeZoneInfo DefaultTimeZone { get; set; } = TimeZoneInfo.Utc;
}
}

View File

@ -113,17 +113,32 @@ namespace Luban.Common.Utils
public static string MakeGoFullName(string module, string name)
{
return MakeGoNamespace(module) + "_" + name;
return MakeGoNamespace(module) + name;
}
public static string MakePyFullName(string module, string name)
{
return module.Replace('.', '_') + "_" + name;
return module.Replace('.', '_') + name;
}
public static string MakeGDScriptFullName(string module, string name)
{
return UpperCaseFirstChar(module.Replace('.', '_') + name);
}
public static string MakeRustFullName(string module, string name)
{
return MakeGoNamespace(module) + "_" + name;
return MakeGoNamespace(module) + name;
}
public static string MakePbFullName(string module, string name)
{
return MakeGoNamespace(module) + name;
}
public static string MakeFlatBuffersFullName(string module, string name)
{
return MakeGoNamespace(module) + name;
}
public static string MakeNamespace(string module, string subModule)

View File

@ -2,7 +2,9 @@
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<PackageProjectUrl>https://github.com/focus-creative-games/luban</PackageProjectUrl>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
</PropertyGroup>
<ItemGroup>
@ -14,7 +16,9 @@
<ItemGroup>
<PackageReference Include="ClosedXML" Version="0.95.4" />
<PackageReference Include="ExcelDataReader" Version="3.6.0" />
<PackageReference Include="NeoLua" Version="1.3.13" />
<PackageReference Include="MessagePack" Version="2.3.85" />
<PackageReference Include="NeoLua" Version="1.3.14" />
<PackageReference Include="Newtonsoft.Json.Bson" Version="1.0.2" />
<PackageReference Include="Ude.NetStandard" Version="1.2.0" />
<PackageReference Include="YamlDotNet" Version="11.2.1" />
</ItemGroup>

View File

@ -0,0 +1,134 @@
# Rules in this file were initially inferred by Visual Studio IntelliCode from the D:\workspace\luban\src\Luban.Job.Cfg\Source\ codebase based on best match to current usage at 2021/11/10
# You can modify the rules from these initially generated values to suit your own policies
# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference
[*.cs]
#Core editorconfig formatting - indentation
#use soft tabs (spaces) for indentation
indent_style = space
#Formatting - indentation options
#csharp_indent_case_contents_when_block
csharp_indent_case_contents_when_block = false
#Formatting - new line options
#place catch statements on a new line
csharp_new_line_before_catch = true
#place else statements on a new line
csharp_new_line_before_else = true
#require finally statements to be on a new line after the closing brace
csharp_new_line_before_finally = true
#require members of object initializers to be on the same line
csharp_new_line_before_members_in_object_initializers = false
#require braces to be on a new line for object_collection_array_initializers, control_blocks, properties, lambdas, types, methods, and accessors (also known as "Allman" style)
csharp_new_line_before_open_brace = object_collection_array_initializers, control_blocks, properties, lambdas, types, methods, accessors
#Formatting - organize using options
#do not place System.* using directives before other using directives
dotnet_sort_system_directives_first = false
#Formatting - spacing options
#require NO space between a cast and the value
csharp_space_after_cast = false
#require a space before the colon for bases or interfaces in a type declaration
csharp_space_after_colon_in_inheritance_clause = true
#require a space after a keyword in a control flow statement such as a for loop
csharp_space_after_keywords_in_control_flow_statements = true
#require a space before the colon for bases or interfaces in a type declaration
csharp_space_before_colon_in_inheritance_clause = true
#remove space within empty argument list parentheses
csharp_space_between_method_call_empty_parameter_list_parentheses = false
#remove space between method call name and opening parenthesis
csharp_space_between_method_call_name_and_opening_parenthesis = false
#do not place space characters after the opening parenthesis and before the closing parenthesis of a method call
csharp_space_between_method_call_parameter_list_parentheses = false
#remove space within empty parameter list parentheses for a method declaration
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
#place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list.
csharp_space_between_method_declaration_parameter_list_parentheses = false
#Formatting - wrapping options
#leave code block on single line
csharp_preserve_single_line_blocks = true
#leave statements and member declarations on the same line
csharp_preserve_single_line_statements = true
#Style - Code block preferences
#prefer curly braces even for one line of code
csharp_prefer_braces = true:suggestion
#Style - expression bodied member options
#prefer expression-bodied members for accessors
csharp_style_expression_bodied_accessors = true:suggestion
#prefer block bodies for constructors
csharp_style_expression_bodied_constructors = false:suggestion
#prefer block bodies for methods
csharp_style_expression_bodied_methods = false:suggestion
#prefer expression-bodied members for properties
csharp_style_expression_bodied_properties = true:suggestion
#Style - expression level options
#prefer out variables to be declared inline in the argument list of a method call when possible
csharp_style_inlined_variable_declaration = true:suggestion
#prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them
dotnet_style_predefined_type_for_member_access = true:suggestion
#Style - Expression-level preferences
#prefer objects to be initialized using object initializers when possible
dotnet_style_object_initializer = true:suggestion
#prefer inferred tuple element names
dotnet_style_prefer_inferred_tuple_names = true:suggestion
#Style - implicit and explicit types
#prefer var over explicit type in all cases, unless overridden by another code style rule
csharp_style_var_elsewhere = true:suggestion
#prefer explicit type over var to declare variables with built-in system types such as int
csharp_style_var_for_built_in_types = false:suggestion
#prefer var when the type is already mentioned on the right-hand side of a declaration expression
csharp_style_var_when_type_is_apparent = true:suggestion
#Style - language keyword and framework type options
#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
#Style - Miscellaneous preferences
#prefer local functions over anonymous functions
csharp_style_pattern_local_over_anonymous_function = true:suggestion
#Style - modifier options
#prefer accessibility modifiers to be declared except for public interface members. This will currently not differ from always and will act as future proofing for if C# adds default interface methods.
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
#Style - Modifier preferences
#when this rule is set to a list of modifiers, prefer the specified ordering.
csharp_preferred_modifier_order = public,private,internal,static,readonly,async,override,new:suggestion
#Style - Pattern matching
#prefer pattern matching instead of is expression with type casts
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
#Style - qualification options
#prefer fields not to be prefaced with this. or Me. in Visual Basic
dotnet_style_qualification_for_field = false:suggestion
#prefer methods not to be prefaced with this. or Me. in Visual Basic
dotnet_style_qualification_for_method = false:suggestion
#prefer properties not to be prefaced with this. or Me. in Visual Basic
dotnet_style_qualification_for_property = false:suggestion

View File

@ -0,0 +1,30 @@
using Luban.Job.Cfg.DataSources.Excel;
using System.Collections.Concurrent;
namespace Luban.Job.Cfg.Cache
{
class ExcelTableValueTypeDefInfoCacheManager
{
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
public static ExcelTableValueTypeDefInfoCacheManager Instance { get; } = new();
private readonly ConcurrentDictionary<(string MD5, string Sheet), RawSheetTableDefInfo> _cacheDefs = new();
public bool TryGetTableDefInfo(string md5, string sheet, out RawSheetTableDefInfo defInfo)
{
if (_cacheDefs.TryGetValue((md5, sheet), out defInfo))
{
s_logger.Debug("find RawSheetTableDefInfo in cache. md5:{} sheet:{} def:{@}", md5, sheet, defInfo);
return true;
}
return false;
}
public void AddTableDefInfoToCache(string md5, string sheet, RawSheetTableDefInfo defInfo)
{
_cacheDefs.TryAdd((md5, sheet), defInfo);
s_logger.Debug("add RawSheetTableDefInfo in cache. md5:{} sheet:{} def:{@}", md5, sheet, defInfo);
}
}
}

View File

@ -11,7 +11,7 @@ namespace Luban.Job.Cfg.Cache
/// 配置加载记录缓存。
/// 如果某个表对应的数据文件未修改,定义没变化,那加载后的数据应该是一样的。
/// </summary>
class FileRecordCacheManager
public class FileRecordCacheManager
{
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
@ -36,14 +36,21 @@ namespace Luban.Job.Cfg.Cache
}
}
public void Init(bool enableCache)
{
_enableCache = enableCache;
}
private readonly ConcurrentDictionary<(string TableName, string MD5, string SheetName), FileRecordCache> _caches = new();
private readonly object _shrinkLocker = new object();
private bool _enableCache = true;
public bool TryGetCacheLoadedRecords(DefTable table, string md5, string originFile, string sheetName, out List<Record> cacheRecords)
{
cacheRecords = null;
if (!_caches.TryGetValue((table.FullName, md5, sheetName), out var r))
if (!_enableCache || !_caches.TryGetValue((table.FullName, md5, sheetName), out var r))
{
return false;
}
@ -77,7 +84,7 @@ namespace Luban.Job.Cfg.Cache
public bool TryGetRecordOutputData(DefTable table, List<Record> records, string dataType, out string md5)
{
if (_tableCaches.TryGetValue((table.FullName, dataType), out var cacheInfo))
if (_enableCache && _tableCaches.TryGetValue((table.FullName, dataType), out var cacheInfo))
{
var cacheAss = cacheInfo.Table.Assembly;
var curAss = table.Assembly;

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Luban.Job.Cfg
{
public static class CfgConstStrings
{
public const string EditorTextTypeName = "Bright.Config.EditorText";
}
}

View File

@ -11,7 +11,7 @@ using System.Threading.Tasks;
namespace Luban.Job.Cfg.DataConverts
{
public class FillSheetVisitor : IDataFuncVisitor<Title, int>
public class FillSheetVisitor : IDataFuncVisitor<TType, Title, int>
{
private readonly List<object[]> _cells;
@ -36,107 +36,107 @@ namespace Luban.Job.Cfg.DataConverts
_cells[_startRowIndex][title.FromIndex] = value;
}
public int Accept(DBool type, Title x)
public int Accept(DBool data, TType type, Title x)
{
SetTitleValue(x, type.Value);
SetTitleValue(x, data.Value);
return 1;
}
public int Accept(DByte type, Title x)
public int Accept(DByte data, TType type, Title x)
{
SetTitleValue(x, type.Value);
SetTitleValue(x, data.Value);
return 1;
}
public int Accept(DShort type, Title x)
public int Accept(DShort data, TType type, Title x)
{
SetTitleValue(x, type.Value);
SetTitleValue(x, data.Value);
return 1;
}
public int Accept(DFshort type, Title x)
public int Accept(DFshort data, TType type, Title x)
{
SetTitleValue(x, type.Value);
SetTitleValue(x, data.Value);
return 1;
}
public int Accept(DInt type, Title x)
public int Accept(DInt data, TType type, Title x)
{
SetTitleValue(x, type.Value);
SetTitleValue(x, data.Value);
return 1;
}
public int Accept(DFint type, Title x)
public int Accept(DFint data, TType type, Title x)
{
SetTitleValue(x, type.Value);
SetTitleValue(x, data.Value);
return 1;
}
public int Accept(DLong type, Title x)
public int Accept(DLong data, TType type, Title x)
{
SetTitleValue(x, type.Value);
SetTitleValue(x, data.Value);
return 1;
}
public int Accept(DFlong type, Title x)
public int Accept(DFlong data, TType type, Title x)
{
SetTitleValue(x, type.Value);
SetTitleValue(x, data.Value);
return 1;
}
public int Accept(DFloat type, Title x)
public int Accept(DFloat data, TType type, Title x)
{
SetTitleValue(x, type.Value);
SetTitleValue(x, data.Value);
return 1;
}
public int Accept(DDouble type, Title x)
public int Accept(DDouble data, TType type, Title x)
{
SetTitleValue(x, type.Value);
SetTitleValue(x, data.Value);
return 1;
}
public int Accept(DEnum type, Title x)
public int Accept(DEnum data, TType type, Title x)
{
SetTitleValue(x, type.StrValue);
SetTitleValue(x, data.StrValue);
return 1;
}
public int Accept(DString type, Title x)
public int Accept(DString data, TType type, Title x)
{
SetTitleValue(x, type.Value);
SetTitleValue(x, data.Value);
return 1;
}
public int Accept(DBytes type, Title x)
public int Accept(DBytes data, TType type, Title x)
{
throw new NotImplementedException();
}
public int Accept(DText type, Title x)
public int Accept(DText data, TType type, Title x)
{
//if (x.FromIndex == x.ToIndex)
//{
// throw new Exception($"title:{x.Name}为text类型至少要占两列");
//}
SetTitleValue(x, type.Apply(ToExcelStringVisitor.Ins, x.Sep));
SetTitleValue(x, data.Apply(ToExcelStringVisitor.Ins, type.OrTag("sep", "#")));
//(_cells[_startRowIndex, x.FromIndex + 1] as Range).Value = type.RawValue;
return 1;
}
public int Accept(DBean type, Title x)
public int Accept(DBean data, TType type, Title x)
{
if (x.SubTitleList.Count > 0)
{
if (type.Type.IsAbstractType)
if (data.Type.IsAbstractType)
{
if (!x.SubTitles.TryGetValue(DefBean.TYPE_NAME_KEY, out var typeTitle))
if (!x.SubTitles.TryGetValue(DefBean.EXCEL_TYPE_NAME_KEY, out var typeTitle) && !x.SubTitles.TryGetValue(DefBean.FALLBACK_TYPE_NAME_KEY, out typeTitle))
{
throw new Exception($"多态bean:{type.Type.FullName} 缺失 __type__ 标题列");
throw new Exception($"多态bean:{data.Type.FullName} 缺失 {DefBean.EXCEL_TYPE_NAME_KEY} 标题列");
}
if (type.ImplType != null)
if (data.ImplType != null)
{
SetTitleValue(typeTitle, type.ImplType.Name);
SetTitleValue(typeTitle, data.ImplType.Name);
}
else
{
@ -145,7 +145,7 @@ namespace Luban.Job.Cfg.DataConverts
}
else
{
if (type.ImplType != null)
if (data.ImplType != null)
{
}
@ -156,22 +156,22 @@ namespace Luban.Job.Cfg.DataConverts
}
}
int rowCount = 1;
if (type.ImplType != null)
if (data.ImplType != null)
{
int index = 0;
foreach (var field in type.ImplType.HierarchyFields)
foreach (var field in data.ImplType.HierarchyFields)
{
var data = type.Fields[index++];
var fdata = data.Fields[index++];
if (!x.SubTitles.TryGetValue(field.Name, out var fieldTitle))
{
throw new Exception($"title:{x.Name} 子title:{field.Name} 缺失");
}
if (data != null)
if (fdata != null)
{
//if (fieldTitle.SubTitleList.Count > 0)
//{
rowCount = Math.Max(rowCount, data.Apply(this, fieldTitle));
rowCount = Math.Max(rowCount, fdata.Apply(this, field.CType, fieldTitle));
//}
//else
//{
@ -181,7 +181,7 @@ namespace Luban.Job.Cfg.DataConverts
}
else if (field.CType is TText)
{
SetTitleValue(fieldTitle, $"null{fieldTitle.Sep}null");
SetTitleValue(fieldTitle, $"null{(field.CType.HasTag("sep") ? field.CType.GetTag("sep") : "#")}null");
}
}
}
@ -189,12 +189,12 @@ namespace Luban.Job.Cfg.DataConverts
}
else
{
SetTitleValue(x, type.Apply(ToExcelStringVisitor.Ins, x.Sep));
SetTitleValue(x, data.Apply(ToExcelStringVisitor.Ins, type.GetTag("sep")));
return 1;
}
}
public int Accept(DArray type, Title x)
public int Accept(DArray data, TType type, Title x)
{
if (x.SelfMultiRows)
{
@ -202,9 +202,10 @@ namespace Luban.Job.Cfg.DataConverts
int totalRow = 0;
try
{
foreach (var ele in type.Datas)
var elementType = data.Type.ElementType;
foreach (var ele in data.Datas)
{
totalRow += ele.Apply(this, x);
totalRow += ele.Apply(this, elementType, x);
_startRowIndex = oldStartRow + totalRow;
}
return totalRow;
@ -216,12 +217,12 @@ namespace Luban.Job.Cfg.DataConverts
}
else
{
SetTitleValue(x, type.Apply(ToExcelStringVisitor.Ins, x.Sep));
SetTitleValue(x, data.Apply(ToExcelStringVisitor.Ins, type.GetTag("sep")));
return 1;
}
}
public int Accept(DList type, Title x)
public int Accept(DList data, TType type, Title x)
{
if (x.SelfMultiRows)
{
@ -229,9 +230,10 @@ namespace Luban.Job.Cfg.DataConverts
int totalRow = 0;
try
{
foreach (var ele in type.Datas)
var elementType = data.Type.ElementType;
foreach (var ele in data.Datas)
{
totalRow += ele.Apply(this, x);
totalRow += ele.Apply(this, elementType, x);
_startRowIndex = oldStartRow + totalRow;
}
return totalRow;
@ -243,47 +245,70 @@ namespace Luban.Job.Cfg.DataConverts
}
else
{
SetTitleValue(x, type.Apply(ToExcelStringVisitor.Ins, x.Sep));
SetTitleValue(x, data.Apply(ToExcelStringVisitor.Ins, type.GetTag("sep")));
return 1;
}
}
public int Accept(DSet type, Title x)
public int Accept(DSet data, TType type, Title x)
{
SetTitleValue(x, type.Apply(ToExcelStringVisitor.Ins, x.Sep));
SetTitleValue(x, data.Apply(ToExcelStringVisitor.Ins, type.GetTag("sep")));
return 1;
}
public int Accept(DMap type, Title x)
public int Accept(DMap data, TType type, Title x)
{
SetTitleValue(x, type.Apply(ToExcelStringVisitor.Ins, x.Sep));
return 1;
if (x.SelfMultiRows)
{
int oldStartRow = _startRowIndex;
int totalRow = 0;
try
{
var elementType = data.Type.ElementType;
foreach (var ele in data.Datas)
{
int row = Math.Max(ele.Key.Apply(this, elementType, x), ele.Value.Apply(this, elementType, x));
totalRow += row;
_startRowIndex = oldStartRow + totalRow;
}
return totalRow;
}
finally
{
_startRowIndex = oldStartRow;
}
}
else
{
SetTitleValue(x, data.Apply(ToExcelStringVisitor.Ins, type.GetTag("sep")));
return 1;
}
}
public int Accept(DVector2 type, Title x)
public int Accept(DVector2 data, TType type, Title x)
{
var v = type.Value;
var v = data.Value;
SetTitleValue(x, $"{v.X}, {v.Y}");
return 1;
}
public int Accept(DVector3 type, Title x)
public int Accept(DVector3 data, TType type, Title x)
{
var v = type.Value;
var v = data.Value;
SetTitleValue(x, $"{v.X},{v.Y},{v.Z}");
return 1;
}
public int Accept(DVector4 type, Title x)
public int Accept(DVector4 data, TType type, Title x)
{
var v = type.Value;
var v = data.Value;
SetTitleValue(x, $"{v.X},{v.Y},{v.Z},{v.W}");
return 1;
}
public int Accept(DDateTime type, Title x)
public int Accept(DDateTime data, TType type, Title x)
{
SetTitleValue(x, type.Time);
SetTitleValue(x, data.Time);
return 1;
}
}

View File

@ -35,7 +35,7 @@ namespace Luban.Job.Cfg.DataConverts
if (type.Type.IsAbstractType)
{
x.WritePropertyName(DefBean.TYPE_NAME_KEY);
x.WritePropertyName(DefBean.JSON_TYPE_NAME_KEY);
x.WriteStringValue(type.ImplType.Name);
}

View File

@ -30,7 +30,7 @@ namespace Luban.Job.Cfg.DataConverts
public override string Accept(DEnum type)
{
return type.Value.ToString();
return $"'{type.StrValue}'";
}
public override string Accept(DBean type)
@ -43,7 +43,7 @@ namespace Luban.Job.Cfg.DataConverts
x.AppendLine("{");
if (type.Type.IsAbstractType)
{
x.Append(subIndent).AppendLine($"_name = '{type.ImplType.Name}',");
x.Append(subIndent).AppendLine($"{DefBean.LUA_TYPE_NAME_KEY} = '{type.ImplType.Name}',");
}
int index = 0;

View File

@ -36,7 +36,7 @@ namespace Luban.Job.Cfg.DataConverts
{
title.AddSubTitle(new Title()
{
Name = "__type__",
Name = DefBean.EXCEL_TYPE_NAME_KEY,
FromIndex = lastColumn + 1,
ToIndex = lastColumn + 1,
Tags = new Dictionary<string, string>(),
@ -164,109 +164,21 @@ namespace Luban.Job.Cfg.DataConverts
title.Tags.Add("sep", "|");
}
}
//int lastColumn = column - 1;
//if (type.IsDynamic)
//{
// title.AddSubTitle(new Title()
// {
// Name = "__type__",
// FromIndex = lastColumn + 1,
// ToIndex = lastColumn + 1,
// Tags = new Dictionary<string, string>(),
// });
// ++lastColumn;
//}
//foreach (var f in type.Bean.HierarchyFields)
//{
// int startColumn = lastColumn + 1;
// var subTitle = new Title()
// {
// Name = f.Name,
// FromIndex = startColumn,
// ToIndex = startColumn,
// Tags = new Dictionary<string, string>(),
// };
// if (f.CType.Tags.TryGetValue("sep", out var sep))
// {
// subTitle.Tags.Add("sep", sep);
// }
// if (f.CType.Tags.TryGetValue("multi_rows", out var multiRows))
// {
// subTitle.Tags.Add("multiRows", multiRows);
// }
// f.CType.Apply(this, subTitle, startColumn);
// lastColumn = subTitle.ToIndex;
// title.AddSubTitle(subTitle);
//}
//title.ToIndex = Math.Max(lastColumn, column);
//int maxFieldNum = 20;
//if (type.IsDynamic)
//{
// if (!title.Tags.ContainsKey("sep"))
// {
// title.Tags.Add("sep", "|");
// }
// //var maxFieldCount = type.Bean.HierarchyNotAbstractChildren.Max(c => c.HierarchyFields.Count);
// //var fields = type.Bean.HierarchyNotAbstractChildren.SelectMany(c => c.HierarchyFields).Select(f => f.Name).ToHashSet();
// title.ToIndex = column;
//}
//else
//{
// int lastColumn = column - 1;
// foreach (var f in type.Bean.HierarchyFields)
// {
// int startColumn = lastColumn + 1;
// var subTitle = new Title()
// {
// Name = f.Name,
// FromIndex = startColumn,
// ToIndex = startColumn,
// Tags = new Dictionary<string, string>(),
// };
// if (f.CType.Tags.TryGetValue("sep", out var sep))
// {
// subTitle.Tags.Add("sep", sep);
// }
// if (f.CType.Tags.TryGetValue("multi_rows", out var multiRows))
// {
// subTitle.Tags.Add("multiRows", multiRows);
// }
// f.CType.Apply(this, subTitle, startColumn);
// lastColumn = subTitle.ToIndex;
// title.AddSubTitle(subTitle);
// }
// title.ToIndex = Math.Max(lastColumn, column);
//}
}
public void Accept(TArray type, Title title, int column)
{
if (!type.ElementType.Apply(IsNotSepTypeVisitor.Ins))
{
title.Tags.TryAdd("sep", "|");
}
title.Tags.TryAdd("sep", "|");
}
public void Accept(TList type, Title title, int column)
{
if (!type.ElementType.Apply(IsNotSepTypeVisitor.Ins))
{
title.Tags.TryAdd("sep", "|");
}
title.Tags.TryAdd("sep", "|");
}
public void Accept(TSet type, Title title, int column)
{
if (!type.ElementType.Apply(IsNotSepTypeVisitor.Ins))
{
title.Tags.TryAdd("sep", "|");
}
title.Tags.TryAdd("sep", "|");
}
public void Accept(TMap type, Title title, int column)

View File

@ -97,6 +97,10 @@ namespace Luban.Job.Cfg.DataConverts
{
sep = type.Type.Sep;
}
else if (string.IsNullOrWhiteSpace(sep))
{
sep = "|";
}
var sb = new List<string>();
if (type.Type.IsAbstractType)
{
@ -133,33 +137,37 @@ namespace Luban.Job.Cfg.DataConverts
public string Accept(DArray type, string sep)
{
if (string.IsNullOrEmpty(sep) && type.Type.ElementType.Apply(IsNotSepTypeVisitor.Ins))
if (string.IsNullOrEmpty(sep))
{
sep = ";";
sep = "|";
}
return string.Join(sep, type.Datas.Select(d => d.Apply(this, sep)));
}
public string Accept(DList type, string sep)
{
if (string.IsNullOrEmpty(sep) && type.Type.ElementType.Apply(IsNotSepTypeVisitor.Ins))
if (string.IsNullOrEmpty(sep))
{
sep = ",";
sep = "|";
}
return string.Join(sep, type.Datas.Select(d => d.Apply(this, sep)));
}
public string Accept(DSet type, string sep)
{
if (string.IsNullOrEmpty(sep) && type.Type.ElementType.Apply(IsNotSepTypeVisitor.Ins))
if (string.IsNullOrEmpty(sep))
{
sep = ",";
sep = "|";
}
return string.Join(sep, type.Datas.Select(d => d.Apply(this, sep)));
}
public string Accept(DMap type, string sep)
{
if (string.IsNullOrEmpty(sep))
{
sep = "|";
}
return string.Join(sep, type.Datas.Select(d => $"{d.Key.Apply(this, sep)}{sep}{d.Value.Apply(this, sep)}"));
}

View File

@ -3,6 +3,7 @@ using Luban.Common.Utils;
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.DataSources.Excel;
using Luban.Job.Cfg.Defs;
using Luban.Job.Cfg.TypeVisitors;
using Luban.Job.Cfg.Utils;
using Luban.Job.Common.Types;
using Luban.Job.Common.TypeVisitors;
@ -28,14 +29,7 @@ namespace Luban.Job.Cfg.DataCreators
return b;
}
var s = x.ToString().ToLower().Trim();
switch (s)
{
case "true":
case "是": return true;
case "false":
case "否": return false;
default: throw new InvalidExcelDataException($"{s} 不是 bool 类型的值 (true 或 false)");
}
return DataUtil.ParseExcelBool(s);
}
public DType Accept(TBool type, ExcelStream x)
@ -263,6 +257,8 @@ namespace Luban.Job.Cfg.DataCreators
public DType Accept(TText type, ExcelStream x)
{
//x = SepIfNeed(type, x);
x = TrySep(type, x);
string key = ParseString(x.Read());
if (key == null)
{
@ -281,6 +277,50 @@ namespace Luban.Job.Cfg.DataCreators
return new DText(key, text);
}
public DType Accept(TDateTime type, ExcelStream x)
{
var d = x.Read();
if (CheckNull(type.IsNullable, d))
{
return null;
}
if (d is System.DateTime datetime)
{
return new DDateTime(datetime);
}
return DataUtil.CreateDateTime(d.ToString());
}
public DType Accept(TVector2 type, ExcelStream x)
{
var d = x.Read();
if (CheckNull(type.IsNullable, d))
{
return null;
}
return DataUtil.CreateVector(type, d.ToString());
}
public DType Accept(TVector3 type, ExcelStream x)
{
var d = x.Read();
if (CheckNull(type.IsNullable, d))
{
return null;
}
return DataUtil.CreateVector(type, d.ToString());
}
public DType Accept(TVector4 type, ExcelStream x)
{
var d = x.Read();
if (CheckNull(type.IsNullable, d))
{
return null;
}
return DataUtil.CreateVector(type, d.ToString());
}
private List<DType> CreateBeanFields(DefBean bean, ExcelStream stream)
{
var list = new List<DType>();
@ -313,19 +353,6 @@ namespace Luban.Job.Cfg.DataCreators
return list;
}
//public static ExcelStream SepIfNeed(TType type, ExcelStream stream)
//{
// string sep = DataUtil.GetSep(type);
// if (!string.IsNullOrEmpty(sep))
// {
// return new ExcelStream(stream.ReadCell(), sep);
// }
// else
// {
// return stream;
// }
//}
public DType Accept(TBean type, ExcelStream x)
{
var originBean = (DefBean)type.Bean;
@ -333,6 +360,10 @@ namespace Luban.Job.Cfg.DataCreators
{
x = new ExcelStream(x.ReadCell(), originBean.Sep);
}
else
{
x = TrySep(type, x);
}
if (originBean.IsAbstractType)
{
@ -345,12 +376,7 @@ namespace Luban.Job.Cfg.DataCreators
}
return null;
}
string fullType = TypeUtil.MakeFullName(originBean.Namespace, subType);
DefBean implType = (DefBean)originBean.GetNotAbstractChildType(subType);
if (implType == null)
{
throw new InvalidExcelDataException($"type:{fullType} 不是bean类型");
}
DefBean implType = DataUtil.GetImplTypeByNameOrAlias(originBean, subType);
return new DBean(type, implType, CreateBeanFields(implType, x));
}
else
@ -371,42 +397,55 @@ namespace Luban.Job.Cfg.DataCreators
}
}
private static ExcelStream TrySep(TType type, ExcelStream stream)
{
string sep = type.GetTag("sep");
if (!string.IsNullOrEmpty(sep) && !stream.TryReadEOF())
{
stream = new ExcelStream(stream.ReadCell(), sep);
}
return stream;
}
// 容器类统统不支持 type.IsNullable
// 因为貌似没意义?
public List<DType> ReadList(TType type, ExcelStream stream)
public List<DType> ReadList(TType type, TType eleType, ExcelStream stream)
{
var datas = new List<DType>();
stream = TrySep(type, stream);
while (!stream.TryReadEOF())
{
datas.Add(type.Apply(this, stream));
datas.Add(eleType.Apply(this, stream));
}
return datas;
}
public DType Accept(TArray type, ExcelStream x)
{
return new DArray(type, ReadList(type.ElementType, x));
return new DArray(type, ReadList(type, type.ElementType, x));
}
public DType Accept(TList type, ExcelStream x)
{
return new DList(type, ReadList(type.ElementType, x));
return new DList(type, ReadList(type, type.ElementType, x));
}
public DType Accept(TSet type, ExcelStream x)
{
return new DSet(type, ReadList(type.ElementType, x));
return new DSet(type, ReadList(type, type.ElementType, x));
}
public DType Accept(TMap type, ExcelStream x)
public DType Accept(TMap type, ExcelStream stream)
{
//x = SepIfNeed(type, x);
stream = TrySep(type, stream);
var datas = new Dictionary<DType, DType>();
while (!x.TryReadEOF())
while (!stream.TryReadEOF())
{
var key = type.KeyType.Apply(this, x);
var value = type.ValueType.Apply(this, x);
var key = type.KeyType.Apply(this, stream);
var value = type.ValueType.Apply(this, stream);
if (!datas.TryAdd(key, value))
{
throw new InvalidExcelDataException($"map 的 key:{key} 重复");
@ -414,49 +453,5 @@ namespace Luban.Job.Cfg.DataCreators
}
return new DMap(type, datas);
}
public DType Accept(TVector2 type, ExcelStream x)
{
var d = x.Read();
if (CheckNull(type.IsNullable, d))
{
return null;
}
return DataUtil.CreateVector(type, d.ToString());
}
public DType Accept(TVector3 type, ExcelStream x)
{
var d = x.Read();
if (CheckNull(type.IsNullable, d))
{
return null;
}
return DataUtil.CreateVector(type, d.ToString());
}
public DType Accept(TVector4 type, ExcelStream x)
{
var d = x.Read();
if (CheckNull(type.IsNullable, d))
{
return null;
}
return DataUtil.CreateVector(type, d.ToString());
}
public DType Accept(TDateTime type, ExcelStream x)
{
var d = x.Read();
if (CheckNull(type.IsNullable, d))
{
return null;
}
if (d is System.DateTime datetime)
{
return new DDateTime(datetime);
}
return DataUtil.CreateDateTime(d.ToString());
}
}
}

View File

@ -67,7 +67,7 @@ namespace Luban.Job.Cfg.DataCreators
public DType Accept(TEnum type, JsonElement x, DefAssembly ass)
{
return new DEnum(type, x.GetString());
return new DEnum(type, x.ToString());
}
public DType Accept(TString type, JsonElement x, DefAssembly ass)
@ -112,18 +112,12 @@ namespace Luban.Job.Cfg.DataCreators
DefBean implBean;
if (bean.IsAbstractType)
{
if (!x.TryGetProperty(DefBean.TYPE_NAME_KEY, out var typeNameProp))
if (!x.TryGetProperty(DefBean.JSON_TYPE_NAME_KEY, out var typeNameProp) && !x.TryGetProperty(DefBean.FALLBACK_TYPE_NAME_KEY, out typeNameProp))
{
throw new Exception($"结构:'{bean.FullName}' 是多态类型,必须用 '{DefBean.TYPE_NAME_KEY}' 字段指定 子类名");
throw new Exception($"结构:'{bean.FullName}' 是多态类型,必须用 '{DefBean.JSON_TYPE_NAME_KEY}' 字段指定 子类名");
}
string subType = typeNameProp.GetString();
var fullName = TypeUtil.MakeFullName(bean.Namespace, subType);
var defType = (DefBean)bean.GetNotAbstractChildType(subType);
//if (defType.IsAbstractType)
//{
// throw new Exception($"type:{fullName} 是抽象类. 不能创建实例");
//}
implBean = defType ?? throw new Exception($"type:'{fullName}' 不是合法类型");
implBean = DataUtil.GetImplTypeByNameOrAlias(bean, subType);
}
else
{

View File

@ -153,19 +153,20 @@ namespace Luban.Job.Cfg.DataCreators
DefBean implBean;
if (bean.IsAbstractType)
{
if (!table.ContainsKey(DefBean.TYPE_NAME_KEY))
string subType;
if(table.ContainsKey(DefBean.LUA_TYPE_NAME_KEY))
{
throw new Exception($"结构:{bean.FullName} 是多态类型,必须用 {DefBean.TYPE_NAME_KEY} 字段指定 子类名");
subType = (string)(table[DefBean.LUA_TYPE_NAME_KEY]);
}
var subType = (string)table[DefBean.TYPE_NAME_KEY];
string fullName = TypeUtil.MakeFullName(bean.Namespace, subType);
var defType = (DefBean)bean.GetNotAbstractChildType(subType);
//if (defType.IsAbstractType)
//{
// throw new Exception($"type:{fullName} 是抽象类. 不能创建实例");
//}
implBean = defType ?? throw new Exception($"type:{fullName} 不是合法类型");
else if (table.ContainsKey(DefBean.FALLBACK_TYPE_NAME_KEY))
{
subType = (string)table[DefBean.FALLBACK_TYPE_NAME_KEY];
}
else
{
throw new Exception($"结构:{bean.FullName} 是多态类型,必须用 {DefBean.LUA_TYPE_NAME_KEY} 字段指定 子类名");
}
implBean = DataUtil.GetImplTypeByNameOrAlias(bean, subType);
}
else
{

View File

@ -15,7 +15,7 @@ using System.Threading.Tasks;
namespace Luban.Job.Cfg.DataCreators
{
class SheetDataCreator : ITypeFuncVisitor<Sheet, TitleRow, DType>
class SheetDataCreator : ITypeFuncVisitor<RowColumnSheet, TitleRow, DType>
{
public static SheetDataCreator Ins { get; } = new();
@ -37,7 +37,7 @@ namespace Luban.Job.Cfg.DataCreators
}
}
public DType Accept(TBool type, Sheet sheet, TitleRow row)
public DType Accept(TBool type, RowColumnSheet sheet, TitleRow row)
{
object x = row.Current;
if (CheckNull(type.IsNullable, x))
@ -53,10 +53,10 @@ namespace Luban.Job.Cfg.DataCreators
{
return DBool.ValueOf(v);
}
return DBool.ValueOf(bool.Parse(x.ToString()));
return DBool.ValueOf(DataUtil.ParseExcelBool(x.ToString()));
}
public DType Accept(TByte type, Sheet sheet, TitleRow row)
public DType Accept(TByte type, RowColumnSheet sheet, TitleRow row)
{
object x = row.Current;
if (CheckNull(type.IsNullable, x))
@ -70,7 +70,7 @@ namespace Luban.Job.Cfg.DataCreators
return DByte.ValueOf(byte.Parse(x.ToString()));
}
public DType Accept(TShort type, Sheet sheet, TitleRow row)
public DType Accept(TShort type, RowColumnSheet sheet, TitleRow row)
{
object x = row.Current;
if (CheckNull(type.IsNullable, x))
@ -85,7 +85,7 @@ namespace Luban.Job.Cfg.DataCreators
return DShort.ValueOf(short.Parse(x.ToString()));
}
public DType Accept(TFshort type, Sheet sheet, TitleRow row)
public DType Accept(TFshort type, RowColumnSheet sheet, TitleRow row)
{
object x = row.Current;
if (CheckNull(type.IsNullable, x))
@ -99,7 +99,7 @@ namespace Luban.Job.Cfg.DataCreators
return DFshort.ValueOf(short.Parse(x.ToString()));
}
public DType Accept(TInt type, Sheet sheet, TitleRow row)
public DType Accept(TInt type, RowColumnSheet sheet, TitleRow row)
{
object x = row.Current;
if (CheckNull(type.IsNullable, x))
@ -114,7 +114,7 @@ namespace Luban.Job.Cfg.DataCreators
return DInt.ValueOf(int.Parse(x.ToString()));
}
public DType Accept(TFint type, Sheet sheet, TitleRow row)
public DType Accept(TFint type, RowColumnSheet sheet, TitleRow row)
{
object x = row.Current;
if (CheckNull(type.IsNullable, x))
@ -129,7 +129,7 @@ namespace Luban.Job.Cfg.DataCreators
return DFint.ValueOf(int.Parse(x.ToString()));
}
public DType Accept(TLong type, Sheet sheet, TitleRow row)
public DType Accept(TLong type, RowColumnSheet sheet, TitleRow row)
{
object x = row.Current;
if (CheckNull(type.IsNullable, x))
@ -144,7 +144,7 @@ namespace Luban.Job.Cfg.DataCreators
return DLong.ValueOf(long.Parse(x.ToString()));
}
public DType Accept(TFlong type, Sheet sheet, TitleRow row)
public DType Accept(TFlong type, RowColumnSheet sheet, TitleRow row)
{
object x = row.Current;
if (CheckNull(type.IsNullable, x))
@ -159,7 +159,7 @@ namespace Luban.Job.Cfg.DataCreators
return DFlong.ValueOf(long.Parse(x.ToString()));
}
public DType Accept(TFloat type, Sheet sheet, TitleRow row)
public DType Accept(TFloat type, RowColumnSheet sheet, TitleRow row)
{
object x = row.Current;
if (CheckNull(type.IsNullable, x))
@ -174,7 +174,7 @@ namespace Luban.Job.Cfg.DataCreators
return DFloat.ValueOf(float.Parse(x.ToString()));
}
public DType Accept(TDouble type, Sheet sheet, TitleRow row)
public DType Accept(TDouble type, RowColumnSheet sheet, TitleRow row)
{
object x = row.Current;
if (CheckNull(type.IsNullable, x))
@ -189,18 +189,82 @@ namespace Luban.Job.Cfg.DataCreators
return DDouble.ValueOf(double.Parse(x.ToString()));
}
public DType Accept(TEnum type, Sheet sheet, TitleRow row)
public DType Accept(TEnum type, RowColumnSheet sheet, TitleRow row)
{
object x = row.Current;
if (CheckNull(type.IsNullable, x))
if (row.Row != null)
{
return null;
object x = row.Current;
if (CheckNull(type.IsNullable, x))
{
return null;
}
if (CheckDefault(x))
{
if (type.DefineEnum.IsFlags || type.DefineEnum.HasZeroValueItem)
{
return new DEnum(type, "0");
}
else
{
throw new InvalidExcelDataException($"枚举类:'{type.DefineEnum.FullName}' 没有value为0的枚举项, 不支持默认值");
}
}
return new DEnum(type, x.ToString());
}
if (x == null)
else if (row.Rows != null)
{
throw new InvalidExcelDataException($"枚举值不能为空");
throw new Exception($"{type.DefineEnum.FullName} 不支持多行格式");
}
else if (row.Fields != null)
{
//throw new Exception($"array 不支持 子字段. 忘记将字段设为多行模式? {row.SelfTitle.Name} => *{row.SelfTitle.Name}");
var items = new List<string>();
var sortedFields = row.Fields.Values.ToList();
sortedFields.Sort((a, b) => a.SelfTitle.FromIndex - b.SelfTitle.FromIndex);
foreach (var field in sortedFields)
{
string itemName = field.SelfTitle.Name;
if (!type.DefineEnum.TryValueByNameOrAlias(itemName, out _))
{
throw new Exception($"列名:{itemName} 不是枚举类型'{type.DefineEnum.FullName}'的有效枚举项");
}
if (field.IsBlank)
{
continue;
}
string cur = field.Current.ToString().ToLower();
if (cur != "0" && cur != "false")
{
items.Add(itemName);
}
}
if (items.Count == 0)
{
if (type.IsNullable)
{
return null;
}
if (type.DefineEnum.IsFlags || type.DefineEnum.HasZeroValueItem)
{
return new DEnum(type, "0");
}
else
{
throw new InvalidExcelDataException($"枚举类:'{type.DefineEnum.FullName}' 没有value为0的枚举项, 不支持默认值");
}
}
return new DEnum(type, string.Join('|', items));
}
else if (row.Elements != null)
{
throw new Exception($"{type.DefineEnum.FullName} 不支持多行子字段格式");
}
else
{
throw new Exception();
}
return new DEnum(type, x.ToString());
}
@ -220,7 +284,7 @@ namespace Luban.Job.Cfg.DataCreators
}
}
public DType Accept(TString type, Sheet sheet, TitleRow row)
public DType Accept(TString type, RowColumnSheet sheet, TitleRow row)
{
object x = row.Current;
if (CheckDefault(x))
@ -242,14 +306,14 @@ namespace Luban.Job.Cfg.DataCreators
return DString.ValueOf(s);
}
public DType Accept(TBytes type, Sheet sheet, TitleRow row)
public DType Accept(TBytes type, RowColumnSheet sheet, TitleRow row)
{
throw new NotImplementedException();
throw new NotSupportedException();
}
public DType Accept(TText type, Sheet sheet, TitleRow row)
public DType Accept(TText type, RowColumnSheet sheet, TitleRow row)
{
if (string.IsNullOrEmpty(row.SelfTitle.Sep))
if (string.IsNullOrEmpty(row.SelfTitle.SepOr(type.GetTag("sep"))))
{
if (row.CellCount != 2)
{
@ -267,12 +331,71 @@ namespace Luban.Job.Cfg.DataCreators
}
else
{
var s = row.AsStream("");
var s = row.AsStream(row.SelfTitle.Sep);
return type.Apply(ExcelStreamDataCreator.Ins, s);
}
}
private List<DType> CreateBeanFields(DefBean bean, Sheet sheet, TitleRow row)
public DType Accept(TDateTime type, RowColumnSheet sheet, TitleRow row)
{
var d = row.Current;
if (CheckNull(type.IsNullable, d))
{
return null;
}
if (d is System.DateTime datetime)
{
return new DDateTime(datetime);
}
return DataUtil.CreateDateTime(d.ToString());
}
public DType Accept(TVector2 type, RowColumnSheet sheet, TitleRow row)
{
var d = row.Current;
if (CheckNull(type.IsNullable, d))
{
return null;
}
if (CheckDefault(d))
{
ThrowIfNonEmpty(row);
return DVector2.Default;
}
return DataUtil.CreateVector(type, d.ToString());
}
public DType Accept(TVector3 type, RowColumnSheet sheet, TitleRow row)
{
var d = row.Current;
if (CheckNull(type.IsNullable, d))
{
return null;
}
if (CheckDefault(d))
{
ThrowIfNonEmpty(row);
return DVector3.Default;
}
return DataUtil.CreateVector(type, d.ToString());
}
public DType Accept(TVector4 type, RowColumnSheet sheet, TitleRow row)
{
var d = row.Current;
if (CheckNull(type.IsNullable, d))
{
return null;
}
if (CheckDefault(d))
{
ThrowIfNonEmpty(row);
return DVector4.Default;
}
return DataUtil.CreateVector(type, d.ToString());
}
private List<DType> CreateBeanFields(DefBean bean, RowColumnSheet sheet, TitleRow row)
{
var list = new List<DType>();
foreach (DefField f in bean.HierarchyFields)
@ -303,13 +426,12 @@ namespace Luban.Job.Cfg.DataCreators
return list;
}
public DType Accept(TBean type, Sheet sheet, TitleRow row)
public DType Accept(TBean type, RowColumnSheet sheet, TitleRow row)
{
//string sep = DataUtil.GetSep(type);
string sep = row.SelfTitle.Sep;// type.GetBeanAs<DefBean>().Sep;
if (row.Row != null)
{
var s = row.AsStream("");
var s = row.AsStream(sep);
if (type.IsNullable && s.TryReadEOF())
{
return null;
@ -318,7 +440,7 @@ namespace Luban.Job.Cfg.DataCreators
}
else if (row.Rows != null)
{
var s = row.AsMultiRowConcatStream("");
var s = row.AsMultiRowConcatStream(sep);
if (type.IsNullable && s.TryReadEOF())
{
return null;
@ -327,38 +449,75 @@ namespace Luban.Job.Cfg.DataCreators
}
else if (row.Fields != null)
{
sep += type.GetBeanAs<DefBean>().Sep;
var originBean = (DefBean)type.Bean;
if (originBean.IsAbstractType)
{
string subType = row.GetSubTitleNamedRow(DefBean.TYPE_NAME_KEY).Current.ToString().Trim();
if (subType.ToLower() == DefBean.BEAN_NULL_STR)
TitleRow typeTitle = row.GetSubTitleNamedRow(DefBean.EXCEL_TYPE_NAME_KEY) ?? row.GetSubTitleNamedRow(DefBean.FALLBACK_TYPE_NAME_KEY);
if (typeTitle == null)
{
throw new Exception($"type:'{originBean.FullName}' 是多态类型,需要定义'{DefBean.EXCEL_TYPE_NAME_KEY}'列来指定具体子类型");
}
TitleRow valueTitle = row.GetSubTitleNamedRow(DefBean.EXCEL_VALUE_NAME_KEY);
sep += type.GetTag("sep");
string subType = typeTitle.Current?.ToString()?.Trim();
if (subType == null || subType == DefBean.BEAN_NULL_STR)
{
if (!type.IsNullable)
{
throw new Exception($"type:'{type}' 不是可空类型 '{type.Bean.FullName}?' , 不能为空");
throw new Exception($"type:'{originBean.FullName}' 不是可空类型 '{type.Bean.FullName}?' , 不能为空");
}
return null;
}
string fullType = TypeUtil.MakeFullName(originBean.Namespace, subType);
DefBean implType = (DefBean)originBean.GetNotAbstractChildType(subType);
if (implType == null)
DefBean implType = DataUtil.GetImplTypeByNameOrAlias(originBean, subType);
if (valueTitle == null)
{
throw new Exception($"type:'{fullType}' 不是 bean 类型");
return new DBean(type, implType, CreateBeanFields(implType, sheet, row));
}
else
{
sep += valueTitle.SelfTitle.Sep;
if (valueTitle.Row != null)
{
var s = valueTitle.AsStream(sep);
if (type.IsNullable && s.TryReadEOF())
{
return null;
}
return new DBean(type, implType, CreateBeanFields(implType, s));
}
else if (valueTitle.Rows != null)
{
var s = valueTitle.AsMultiRowConcatStream(sep);
if (type.IsNullable && s.TryReadEOF())
{
return null;
}
return new DBean(type, implType, CreateBeanFields(implType, s));
}
else
{
throw new Exception();
}
}
return new DBean(type, implType, CreateBeanFields(implType, sheet, row));
}
else
{
if (type.IsNullable)
{
string subType = row.GetSubTitleNamedRow(DefBean.TYPE_NAME_KEY).Current.ToString().Trim();
if (subType == DefBean.BEAN_NULL_STR)
TitleRow typeTitle = row.GetSubTitleNamedRow(DefBean.EXCEL_TYPE_NAME_KEY) ?? row.GetSubTitleNamedRow(DefBean.FALLBACK_TYPE_NAME_KEY);
if (typeTitle == null)
{
throw new Exception($"type:'{originBean.FullName}' 是可空类型,需要定义'{DefBean.EXCEL_TYPE_NAME_KEY}'列来指明是否可空");
}
string subType = typeTitle.Current?.ToString()?.Trim();
if (subType == null || subType == DefBean.BEAN_NULL_STR)
{
return null;
}
else if (subType != DefBean.BEAN_NOT_NULL_STR && subType != originBean.Name)
{
throw new Exception($"type:'{type.Bean.FullName}' 可空标识:'{subType}' 不合法(只能为{DefBean.BEAN_NOT_NULL_STR}或{DefBean.BEAN_NULL_STR}或{originBean.Name})");
throw new Exception($"type:'{originBean.FullName}' 可空标识:'{subType}' 不合法(只能为'{DefBean.BEAN_NULL_STR}''{DefBean.BEAN_NOT_NULL_STR}''{originBean.Name}')");
}
}
@ -367,7 +526,7 @@ namespace Luban.Job.Cfg.DataCreators
}
else if (row.Elements != null)
{
var s = row.AsMultiRowConcatElements();
var s = row.AsMultiRowConcatElements(sep);
return type.Apply(ExcelStreamDataCreator.Ins, s);
}
else
@ -378,10 +537,18 @@ namespace Luban.Job.Cfg.DataCreators
public List<DType> ReadList(TType type, ExcelStream stream)
{
var sep = type.GetTag("sep");
var datas = new List<DType>();
while (!stream.TryReadEOF())
{
datas.Add(type.Apply(ExcelStreamDataCreator.Ins, stream));
if (string.IsNullOrEmpty(sep))
{
datas.Add(type.Apply(ExcelStreamDataCreator.Ins, stream));
}
else
{
datas.Add(type.Apply(ExcelStreamDataCreator.Ins, new ExcelStream(stream.ReadCell(), sep)));
}
}
return datas;
}
@ -399,103 +566,69 @@ namespace Luban.Job.Cfg.DataCreators
return datas;
}
public DType Accept(TArray type, Sheet sheet, TitleRow row)
private List<DType> ReadCollectionDatas(TType type, TType elementType, RowColumnSheet sheet, TitleRow row)
{
if (row.Row != null)
{
var s = row.AsStream(row.SelfTitle.Sep);
return ExcelStreamDataCreator.Ins.ReadList(type, elementType, s);
}
else if (row.Rows != null)
{
var s = row.AsMultiRowStream(row.SelfTitle.Sep);
return ReadList(elementType, s);
}
else if (row.Fields != null)
{
//throw new Exception($"array 不支持 子字段. 忘记将字段设为多行模式? {row.SelfTitle.Name} => *{row.SelfTitle.Name}");
var datas = new List<DType>(row.Fields.Count);
var sortedFields = row.Fields.Values.ToList();
sortedFields.Sort((a, b) => a.SelfTitle.FromIndex - b.SelfTitle.FromIndex);
foreach (var field in sortedFields)
{
if (field.IsBlank)
{
continue;
}
datas.Add(elementType.Apply(this, sheet, field));
}
return datas;
}
else if (row.Elements != null)
{
return row.Elements.Select(e => elementType.Apply(this, sheet, e)).ToList();
}
else
{
throw new Exception();
}
}
public DType Accept(TArray type, RowColumnSheet sheet, TitleRow row)
{
//string sep = DataUtil.GetSep(type);
if (row.Row != null)
{
var s = row.AsStream(DataUtil.GetTypeSep(type.ElementType));
return new DArray(type, ReadList(type.ElementType, s));
}
else if (row.Rows != null)
{
var s = row.AsMultiRowStream(DataUtil.GetTypeSep(type.ElementType));
return new DArray(type, ReadList(type.ElementType, s));
}
else if (row.Fields != null)
{
throw new Exception($"array 不支持 子字段. 忘记将字段设为多行模式? {row.SelfTitle.Name} => *{row.SelfTitle.Name}");
}
else if (row.Elements != null)
{
return new DArray(type, row.Elements.Select(e => type.ElementType.Apply(this, sheet, e)).ToList());
}
else
{
throw new Exception();
}
return new DArray(type, ReadCollectionDatas(type, type.ElementType, sheet, row));
}
public DType Accept(TList type, Sheet sheet, TitleRow row)
public DType Accept(TList type, RowColumnSheet sheet, TitleRow row)
{
if (row.Row != null)
{
var s = row.AsStream(DataUtil.GetTypeSep(type.ElementType));
return new DList(type, ReadList(type.ElementType, s));
}
else if (row.Rows != null)
{
var s = row.AsMultiRowStream(DataUtil.GetTypeSep(type.ElementType));
return new DList(type, ReadList(type.ElementType, s));
}
else if (row.Fields != null)
{
throw new Exception($"list 不支持 子字段. 忘记将字段设为多行模式? {row.SelfTitle.Name} => *{row.SelfTitle.Name}");
}
else if (row.Elements != null)
{
return new DList(type, row.Elements.Select(e => type.ElementType.Apply(this, sheet, e)).ToList());
}
else
{
throw new Exception();
}
return new DList(type, ReadCollectionDatas(type, type.ElementType, sheet, row));
}
public DType Accept(TSet type, Sheet sheet, TitleRow row)
public DType Accept(TSet type, RowColumnSheet sheet, TitleRow row)
{
if (row.Row != null)
{
var s = row.AsStream(DataUtil.GetTypeSep(type.ElementType));
return new DSet(type, ReadList(type.ElementType, s));
}
else if (row.Rows != null)
{
var s = row.AsMultiRowStream(DataUtil.GetTypeSep(type.ElementType));
return new DSet(type, ReadList(type.ElementType, s));
}
else if (row.Fields != null)
{
throw new Exception($"set 不支持 子字段");
}
else if (row.Elements != null)
{
throw new NotSupportedException();
}
else
{
throw new Exception();
}
return new DSet(type, ReadCollectionDatas(type, type.ElementType, sheet, row));
}
public DType Accept(TMap type, Sheet sheet, TitleRow row)
public DType Accept(TMap type, RowColumnSheet sheet, TitleRow row)
{
string sep = "";
string sep = row.SelfTitle.Sep;
if (row.Row != null)
{
var s = row.AsStream(sep);
var datas = new Dictionary<DType, DType>();
while (!s.TryReadEOF())
{
var key = type.KeyType.Apply(ExcelStreamDataCreator.Ins, s);
var value = type.ValueType.Apply(ExcelStreamDataCreator.Ins, s);
datas.Add(key, value);
}
return new DMap(type, datas);
return type.Apply(ExcelStreamDataCreator.Ins, s);
}
else if (row.Rows != null)
{
@ -517,11 +650,11 @@ namespace Luban.Job.Cfg.DataCreators
foreach (var e in row.Fields)
{
var keyData = type.KeyType.Apply(StringDataCreator.Ins, e.Key);
if (Sheet.IsBlankRow(e.Value.Row, e.Value.SelfTitle.FromIndex, e.Value.SelfTitle.ToIndex))
if (RowColumnSheet.IsBlankRow(e.Value.Row, e.Value.SelfTitle.FromIndex, e.Value.SelfTitle.ToIndex))
{
continue;
}
var valueData = type.ValueType.Apply(ExcelStreamDataCreator.Ins, e.Value.AsStream(sep));
var valueData = type.ValueType.Apply(ExcelStreamDataCreator.Ins, e.Value.AsStream(""));
datas.Add(keyData, valueData);
}
return new DMap(type, datas);
@ -531,7 +664,7 @@ namespace Luban.Job.Cfg.DataCreators
var datas = new Dictionary<DType, DType>();
foreach (var e in row.Elements)
{
var stream = e.AsStream("");
var stream = e.AsStream(sep);
var keyData = type.KeyType.Apply(ExcelStreamDataCreator.Ins, stream);
var valueData = type.ValueType.Apply(ExcelStreamDataCreator.Ins, stream);
datas.Add(keyData, valueData);
@ -544,63 +677,28 @@ namespace Luban.Job.Cfg.DataCreators
}
}
public DType Accept(TVector2 type, Sheet sheet, TitleRow row)
private List<DType> CreateBeanFields(DefBean bean, ExcelStream stream)
{
var d = row.Current;
if (CheckNull(type.IsNullable, d))
var list = new List<DType>();
foreach (DefField f in bean.HierarchyFields)
{
return null;
try
{
list.Add(f.CType.Apply(ExcelStreamDataCreator.Ins, stream));
}
catch (DataCreateException dce)
{
dce.Push(bean, f);
throw;
}
catch (Exception e)
{
var dce = new DataCreateException(e, stream.LastReadDataInfo);
dce.Push(bean, f);
throw dce;
}
}
if (CheckDefault(d))
{
ThrowIfNonEmpty(row);
return DVector2.Default;
}
return DataUtil.CreateVector(type, d.ToString());
}
public DType Accept(TVector3 type, Sheet sheet, TitleRow row)
{
var d = row.Current;
if (CheckNull(type.IsNullable, d))
{
return null;
}
if (CheckDefault(d))
{
ThrowIfNonEmpty(row);
return DVector3.Default;
}
return DataUtil.CreateVector(type, d.ToString());
}
public DType Accept(TVector4 type, Sheet sheet, TitleRow row)
{
var d = row.Current;
if (CheckNull(type.IsNullable, d))
{
return null;
}
if (CheckDefault(d))
{
ThrowIfNonEmpty(row);
return DVector4.Default;
}
return DataUtil.CreateVector(type, d.ToString());
}
public DType Accept(TDateTime type, Sheet sheet, TitleRow row)
{
var d = row.Current;
if (CheckNull(type.IsNullable, d))
{
return null;
}
if (d is System.DateTime datetime)
{
return new DDateTime(datetime);
}
return DataUtil.CreateDateTime(d.ToString());
return list;
}
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Luban.Job.Cfg.DataCreators
{
class UnityAssetDataCreator : YamlDataCreator
{
public new static UnityAssetDataCreator Ins = new();
}
}

View File

@ -96,18 +96,16 @@ namespace Luban.Job.Cfg.DataCreators
DefBean implBean;
if (bean.IsAbstractType)
{
string subType = x.Attribute(DefBean.TYPE_NAME_KEY)?.Value;
string subType = x.Attribute(DefBean.XML_TYPE_NAME_KEY)?.Value;
if (string.IsNullOrEmpty(subType))
{
subType = x.Attribute(DefBean.FALLBACK_TYPE_NAME_KEY)?.Value;
}
if (string.IsNullOrWhiteSpace(subType))
{
throw new Exception($"bean:'{bean.FullName}'是多态,需要指定{DefBean.TYPE_NAME_KEY}属性.\n xml:{x}");
throw new Exception($"bean:'{bean.FullName}'是多态,需要指定{DefBean.XML_TYPE_NAME_KEY}属性.\n xml:{x}");
}
var fullName = TypeUtil.MakeFullName(bean.Namespace, subType);
var defType = (DefBean)bean.GetNotAbstractChildType(subType);
//if (defType.IsAbstractType)
//{
// throw new Exception($"type:{fullName} 是抽象类. 不能创建实例");
//}
implBean = defType ?? throw new Exception($"type:'{fullName}' 不是合法类型");
implBean = DataUtil.GetImplTypeByNameOrAlias(bean, subType);
}
else
{

View File

@ -23,7 +23,7 @@ namespace Luban.Job.Cfg.DataCreators
private static string GetTextValue(YamlNode node)
{
return ((string)node);
return node.ToString();
}
public DType Accept(TBool type, YamlNode x, DefAssembly y)
@ -91,8 +91,8 @@ namespace Luban.Job.Cfg.DataCreators
throw new NotSupportedException();
}
private readonly static YamlScalarNode s_keyNodeName = new("key");
private readonly static YamlScalarNode s_textNodeName = new("text");
private static readonly YamlScalarNode s_keyNodeName = new("key");
private static readonly YamlScalarNode s_textNodeName = new("text");
public DType Accept(TText type, YamlNode x, DefAssembly y)
{
@ -102,7 +102,8 @@ namespace Luban.Job.Cfg.DataCreators
return new DText(key, text);
}
private readonly static YamlScalarNode s_typeNodeName = new(DefBean.TYPE_NAME_KEY);
private static readonly YamlScalarNode s_typeNodeName = new(DefBean.JSON_TYPE_NAME_KEY);
private static readonly YamlScalarNode s_typeNodeNameFallback = new(DefBean.FALLBACK_TYPE_NAME_KEY);
public DType Accept(TBean type, YamlNode x, DefAssembly y)
{
@ -112,14 +113,16 @@ namespace Luban.Job.Cfg.DataCreators
DefBean implBean;
if (bean.IsAbstractType)
{
string subType = m.Children.TryGetValue(s_typeNodeName, out var typeNode) ? (string)typeNode : null;
if (!m.Children.TryGetValue(s_typeNodeName, out var typeNode) && !m.Children.TryGetValue(s_typeNodeNameFallback, out typeNode))
{
throw new Exception($"bean:'{bean.FullName}'是多态,需要指定{DefBean.JSON_TYPE_NAME_KEY}属性.\n xml:{x}");
}
string subType = (string)typeNode;
if (string.IsNullOrWhiteSpace(subType))
{
throw new Exception($"bean:'{bean.FullName}'是多态,需要指定{DefBean.TYPE_NAME_KEY}属性.\n xml:{x}");
throw new Exception($"bean:'{bean.FullName}'是多态,需要指定{DefBean.JSON_TYPE_NAME_KEY}属性.\n xml:{x}");
}
var fullName = TypeUtil.MakeFullName(bean.Namespace, subType);
var defType = (DefBean)bean.GetNotAbstractChildType(subType);
implBean = defType ?? throw new Exception($"type:'{fullName}' 不是合法类型");
implBean = DataUtil.GetImplTypeByNameOrAlias(bean, subType);
}
else
{
@ -204,10 +207,10 @@ namespace Luban.Job.Cfg.DataCreators
return new DMap(type, map);
}
private readonly static YamlScalarNode s_xNodeName = new("x");
private readonly static YamlScalarNode s_yNodeName = new("y");
private readonly static YamlScalarNode s_zNodeName = new("z");
private readonly static YamlScalarNode s_wNodeName = new("w");
private static readonly YamlScalarNode s_xNodeName = new("x");
private static readonly YamlScalarNode s_yNodeName = new("y");
private static readonly YamlScalarNode s_zNodeName = new("z");
private static readonly YamlScalarNode s_wNodeName = new("w");
private static float ParseChildFloatValue(YamlNode node, YamlScalarNode keyName)
{

View File

@ -88,8 +88,7 @@ namespace Luban.Job.Cfg.DataExporters
public void Accept(DText type, ByteBuf x)
{
x.WriteString(type.Key);
var ass = DefAssembly.LocalAssebmly;
x.WriteString(type.GetText(ass.ExportTextTable, ass.NotConvertTextSet));
x.WriteString(type.TextOfCurrentAssembly);
}
public void Accept(DBean type, ByteBuf x)
@ -180,7 +179,7 @@ namespace Luban.Job.Cfg.DataExporters
public void Accept(DDateTime type, ByteBuf x)
{
x.WriteInt(type.GetUnixTime(DefAssembly.LocalAssebmly.TimeZone));
x.WriteLong(type.UnixTimeOfCurrentAssembly);
}
}
}

View File

@ -0,0 +1,40 @@
using Bright.Serialization;
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.DataSources;
using Luban.Job.Cfg.DataVisitors;
using Luban.Job.Cfg.Defs;
using Luban.Job.Cfg.RawDefs;
using System;
using System.Collections.Generic;
namespace Luban.Job.Cfg.DataExporters
{
class BinaryIndexExportor
{
public static BinaryIndexExportor Ins { get; } = new BinaryIndexExportor();
public void WriteList(DefTable table, List<Record> datas, ByteBuf x)
{
x.WriteSize(datas.Count);
var tableDataBuf = new ByteBuf(10 * 1024);
tableDataBuf.WriteSize(datas.Count);
foreach (var d in datas)
{
int offset = tableDataBuf.Size;
d.Data.Apply(BinaryExportor.Ins, tableDataBuf);
string keyStr = "";
foreach (IndexInfo index in table.IndexList)
{
DType key = d.Data.Fields[index.IndexFieldIdIndex];
key.Apply(BinaryExportor.Ins, x);
keyStr += key.ToString() + ",";
}
x.WriteSize(offset);
Console.WriteLine($"table:{table.Name} key:{keyStr} offset:{offset}");
}
}
}
}

View File

@ -0,0 +1,203 @@
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.DataSources;
using Luban.Job.Cfg.DataVisitors;
using Luban.Job.Cfg.Defs;
using Luban.Job.Cfg.Utils;
using Newtonsoft.Json.Bson;
using System;
using System.Collections.Generic;
using System.Text.Json;
namespace Luban.Job.Cfg.DataExporters
{
class BsonExportor : IDataActionVisitor<BsonDataWriter>
{
public static BsonExportor Ins { get; } = new BsonExportor();
public void WriteAsArray(List<Record> datas, BsonDataWriter x)
{
x.WriteStartArray();
foreach (var d in datas)
{
d.Data.Apply(this, x);
}
x.WriteEndArray();
}
public void Accept(DBool type, BsonDataWriter x)
{
x.WriteValue(type.Value);
}
public void Accept(DByte type, BsonDataWriter x)
{
x.WriteValue(type.Value);
}
public void Accept(DShort type, BsonDataWriter x)
{
x.WriteValue(type.Value);
}
public void Accept(DFshort type, BsonDataWriter x)
{
x.WriteValue(type.Value);
}
public void Accept(DInt type, BsonDataWriter x)
{
x.WriteValue(type.Value);
}
public void Accept(DFint type, BsonDataWriter x)
{
x.WriteValue(type.Value);
}
public void Accept(DLong type, BsonDataWriter x)
{
x.WriteValue(type.Value);
}
public void Accept(DFlong type, BsonDataWriter x)
{
x.WriteValue(type.Value);
}
public void Accept(DFloat type, BsonDataWriter x)
{
x.WriteValue(type.Value);
}
public void Accept(DDouble type, BsonDataWriter x)
{
x.WriteValue(type.Value);
}
public virtual void Accept(DEnum type, BsonDataWriter x)
{
x.WriteValue(type.Value);
}
public void Accept(DString type, BsonDataWriter x)
{
x.WriteValue(type.Value);
}
public void Accept(DBytes type, BsonDataWriter x)
{
throw new NotImplementedException();
}
public virtual void Accept(DText type, BsonDataWriter x)
{
x.WriteStartObject();
x.WritePropertyName(DText.KEY_NAME);
x.WriteValue(type.Key);
x.WritePropertyName(DText.TEXT_NAME);
x.WriteValue(type.TextOfCurrentAssembly);
x.WriteEndObject();
}
public virtual void Accept(DBean type, BsonDataWriter x)
{
x.WriteStartObject();
if (type.Type.IsAbstractType)
{
x.WritePropertyName(DefBean.JSON_TYPE_NAME_KEY);
x.WriteValue(DataUtil.GetImplTypeName(type));
}
var defFields = type.ImplType.HierarchyFields;
int index = 0;
foreach (var d in type.Fields)
{
var defField = (DefField)defFields[index++];
// 特殊处理 bean 多态类型
// 另外,不生成 xxx:null 这样
if (d == null || !defField.NeedExport)
{
//x.WriteNullValue();
}
else
{
x.WritePropertyName(defField.Name);
d.Apply(this, x);
}
}
x.WriteEndObject();
}
public void WriteList(List<DType> datas, BsonDataWriter x)
{
x.WriteStartArray();
foreach (var d in datas)
{
d.Apply(this, x);
}
x.WriteEndArray();
}
public void Accept(DArray type, BsonDataWriter x)
{
WriteList(type.Datas, x);
}
public void Accept(DList type, BsonDataWriter x)
{
WriteList(type.Datas, x);
}
public void Accept(DSet type, BsonDataWriter x)
{
WriteList(type.Datas, x);
}
public virtual void Accept(DMap type, BsonDataWriter x)
{
x.WriteStartArray();
foreach (var d in type.Datas)
{
x.WriteStartArray();
d.Key.Apply(this, x);
d.Value.Apply(this, x);
x.WriteEndArray();
}
x.WriteEndArray();
}
public void Accept(DVector2 type, BsonDataWriter x)
{
x.WriteStartObject();
x.WritePropertyName("x"); x.WriteValue(type.Value.X);
x.WritePropertyName("y"); x.WriteValue(type.Value.Y);
x.WriteEndObject();
}
public void Accept(DVector3 type, BsonDataWriter x)
{
x.WriteStartObject();
x.WritePropertyName("x"); x.WriteValue(type.Value.X);
x.WritePropertyName("y"); x.WriteValue(type.Value.Y);
x.WritePropertyName("z"); x.WriteValue(type.Value.Z);
x.WriteEndObject();
}
public void Accept(DVector4 type, BsonDataWriter x)
{
x.WriteStartObject();
x.WritePropertyName("x"); x.WriteValue(type.Value.X);
x.WritePropertyName("y"); x.WriteValue(type.Value.Y);
x.WritePropertyName("z"); x.WriteValue(type.Value.Z);
x.WritePropertyName("w"); x.WriteValue(type.Value.W);
x.WriteEndObject();
}
public virtual void Accept(DDateTime type, BsonDataWriter x)
{
x.WriteValue(type.UnixTimeOfCurrentAssembly);
}
}
}

View File

@ -0,0 +1,97 @@
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.DataSources;
using Luban.Job.Cfg.DataVisitors;
using Luban.Job.Cfg.Defs;
using Luban.Job.Common.Types;
using Luban.Job.Common.TypeVisitors;
using System;
using System.Collections.Generic;
using System.Text.Json;
namespace Luban.Job.Cfg.DataExporters
{
class FlatBuffersJsonExportor : JsonExportor
{
public static new FlatBuffersJsonExportor Ins { get; } = new();
public void WriteAsTable(List<Record> datas, Utf8JsonWriter x)
{
x.WriteStartObject();
// 如果修改了这个名字请同时修改table.tpl
x.WritePropertyName("data_list");
x.WriteStartArray();
foreach (var d in datas)
{
d.Data.Apply(this, x);
}
x.WriteEndArray();
x.WriteEndObject();
}
public override void Accept(DText type, Utf8JsonWriter x)
{
// 不支持本地化。只能简单起见这么做了
//x.WriteStartObject();
//x.WritePropertyName(DText.KEY_NAME);
//x.WriteStringValue(type.Key);
//x.WritePropertyName(DText.TEXT_NAME);
x.WriteStringValue(type.TextOfCurrentAssembly);
//x.WriteEndObject();
}
public override void Accept(DBean type, Utf8JsonWriter x)
{
x.WriteStartObject();
// flatc 不允许有多余字段
//if (type.Type.IsAbstractType)
//{
// x.WritePropertyName(DefBean.TYPE_NAME_KEY);
// x.WriteStringValue(type.ImplType.Name);
//}
var defFields = type.ImplType.HierarchyFields;
int index = 0;
foreach (var d in type.Fields)
{
var defField = (DefField)defFields[index++];
// 特殊处理 bean 多态类型
// 另外,不生成 xxx:null 这样
if (d == null || !defField.NeedExport)
{
//x.WriteNullValue();
}
else
{
// flatbuffers的union类型的json格式,会额外产生一个 xx_type字段。
// 另外json格式不支持union出现在容器类型上。
if (d is DBean beanField && beanField.Type.IsAbstractType)
{
x.WritePropertyName($"{defField.Name}_type");
x.WriteStringValue(TBean.Create(defField.CType.IsNullable, beanField.ImplType, null).Apply(FlatBuffersTypeNameVisitor.Ins));
}
x.WritePropertyName(defField.Name);
d.Apply(this, x);
}
}
x.WriteEndObject();
}
public override void Accept(DMap type, Utf8JsonWriter x)
{
x.WriteStartArray();
foreach (var d in type.Datas)
{
x.WriteStartObject();
x.WritePropertyName("key");
d.Key.Apply(this, x);
x.WritePropertyName("value");
d.Value.Apply(this, x);
x.WriteEndObject();
}
x.WriteEndArray();
}
}
}

View File

@ -2,6 +2,7 @@
using Luban.Job.Cfg.DataSources;
using Luban.Job.Cfg.DataVisitors;
using Luban.Job.Cfg.Defs;
using Luban.Job.Cfg.RawDefs;
using System;
using System.Collections.Generic;
using System.Text.Json;
@ -10,18 +11,18 @@ namespace Luban.Job.Cfg.DataExporters
{
class Json2Exportor : JsonExportor
{
public new static Json2Exportor Ins { get; } = new();
public static new Json2Exportor Ins { get; } = new();
public void WriteAsObject(DefTable table, List<Record> datas, Utf8JsonWriter x)
{
switch (table.Mode)
{
case RawDefs.ETableMode.ONE:
case ETableMode.ONE:
{
this.Accept(datas[0].Data, x);
break;
}
case RawDefs.ETableMode.MAP:
case ETableMode.MAP:
{
x.WriteStartObject();
@ -37,6 +38,11 @@ namespace Luban.Job.Cfg.DataExporters
x.WriteEndObject();
break;
}
case ETableMode.LIST:
{
JsonExportor.Ins.WriteAsArray(datas, x);
break;
}
default:
{
throw new NotSupportedException($"not support table mode:{table.Mode}");

View File

@ -2,6 +2,7 @@ using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.DataSources;
using Luban.Job.Cfg.DataVisitors;
using Luban.Job.Cfg.Defs;
using Luban.Job.Cfg.Utils;
using System;
using System.Collections.Generic;
using System.Text.Json;
@ -93,8 +94,7 @@ namespace Luban.Job.Cfg.DataExporters
x.WritePropertyName(DText.KEY_NAME);
x.WriteStringValue(type.Key);
x.WritePropertyName(DText.TEXT_NAME);
var ass = DefAssembly.LocalAssebmly;
x.WriteStringValue(type.GetText(ass.ExportTextTable, ass.NotConvertTextSet));
x.WriteStringValue(type.TextOfCurrentAssembly);
x.WriteEndObject();
}
@ -104,8 +104,8 @@ namespace Luban.Job.Cfg.DataExporters
if (type.Type.IsAbstractType)
{
x.WritePropertyName(DefBean.TYPE_NAME_KEY);
x.WriteStringValue(type.ImplType.Name);
x.WritePropertyName(DefBean.JSON_TYPE_NAME_KEY);
x.WriteStringValue(DataUtil.GetImplTypeName(type));
}
var defFields = type.ImplType.HierarchyFields;
@ -196,7 +196,7 @@ namespace Luban.Job.Cfg.DataExporters
public virtual void Accept(DDateTime type, Utf8JsonWriter x)
{
x.WriteNumberValue(type.GetUnixTime(DefAssembly.LocalAssebmly.TimeZone));
x.WriteNumberValue(type.UnixTimeOfCurrentAssembly);
}
}
}

View File

@ -30,5 +30,18 @@ namespace Luban.Job.Cfg.DataExporters
}
s.Append('}');
}
public void ExportTableList(DefTable t, List<Record> records, StringBuilder s)
{
s.Append("return").AppendLine();
s.Append('{').AppendLine();
foreach (Record r in records)
{
DBean d = r.Data;
s.Append(d.Apply(ToLuaLiteralVisitor.Ins));
s.Append(',').AppendLine();
}
s.Append('}');
}
}
}

View File

@ -0,0 +1,240 @@
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.DataSources;
using Luban.Job.Cfg.DataVisitors;
using Luban.Job.Cfg.Defs;
using Luban.Job.Cfg.Utils;
using MessagePack;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Luban.Job.Cfg.DataExporters
{
class MsgPackExportor
{
public static MsgPackExportor Ins { get; } = new();
public void WriteList(DefTable table, List<Record> records, ref MessagePackWriter writer)
{
writer.WriteArrayHeader(records.Count);
foreach (var record in records)
{
Accept(record.Data, ref writer);
}
}
public void Apply(DType type, ref MessagePackWriter writer)
{
switch (type)
{
case DInt x: Accept(x, ref writer); break;
case DString x: Accept(x, ref writer); break;
case DFloat x: Accept(x, ref writer); break;
case DBean x: Accept(x, ref writer); break;
case DBool x: Accept(x, ref writer); break;
case DEnum x: Accept(x, ref writer); break;
case DList x: Accept(x, ref writer); break;
case DArray x: Accept(x, ref writer); break;
case DLong x: Accept(x, ref writer); break;
case DDateTime x: Accept(x, ref writer); break;
case DMap x: Accept(x, ref writer); break;
case DText x: Accept(x, ref writer); break;
case DVector2 x: Accept(x, ref writer); break;
case DVector3 x: Accept(x, ref writer); break;
case DVector4 x: Accept(x, ref writer); break;
case DByte x: Accept(x, ref writer); break;
case DDouble x: Accept(x, ref writer); break;
case DFint x: Accept(x, ref writer); break;
case DFlong x: Accept(x, ref writer); break;
case DFshort x: Accept(x, ref writer); break;
case DSet x: Accept(x, ref writer); break;
case DShort x: Accept(x, ref writer); break;
default: throw new NotSupportedException($"DType:{type.GetType().FullName} not support");
}
}
public void Accept(DBool type, ref MessagePackWriter writer)
{
writer.Write(type.Value);
}
public void Accept(DByte type, ref MessagePackWriter writer)
{
writer.Write(type.Value);
}
public void Accept(DShort type, ref MessagePackWriter writer)
{
writer.Write(type.Value);
}
public void Accept(DFshort type, ref MessagePackWriter writer)
{
writer.Write(type.Value);
}
public void Accept(DInt type, ref MessagePackWriter writer)
{
writer.Write(type.Value);
}
public void Accept(DFint type, ref MessagePackWriter writer)
{
writer.Write(type.Value);
}
public void Accept(DLong type, ref MessagePackWriter writer)
{
writer.Write(type.Value);
}
public void Accept(DFlong type, ref MessagePackWriter writer)
{
writer.Write(type.Value);
}
public void Accept(DFloat type, ref MessagePackWriter writer)
{
writer.Write(type.Value);
}
public void Accept(DDouble type, ref MessagePackWriter writer)
{
writer.Write(type.Value);
}
public void Accept(DEnum type, ref MessagePackWriter writer)
{
writer.Write(type.Value);
}
public void Accept(DString type, ref MessagePackWriter writer)
{
writer.Write(type.Value);
}
public void Accept(DText type, ref MessagePackWriter writer)
{
writer.WriteArrayHeader(2);
writer.Write(type.Key);
writer.Write(type.TextOfCurrentAssembly);
}
public void Accept(DBytes type, ref MessagePackWriter writer)
{
writer.Write(type.Value);
}
public void Accept(DVector2 type, ref MessagePackWriter writer)
{
writer.WriteArrayHeader(2);
writer.Write(type.Value.X);
writer.Write(type.Value.Y);
}
public void Accept(DVector3 type, ref MessagePackWriter writer)
{
writer.WriteArrayHeader(3);
writer.Write(type.Value.X);
writer.Write(type.Value.Y);
writer.Write(type.Value.Z);
}
public void Accept(DVector4 type, ref MessagePackWriter writer)
{
writer.WriteArrayHeader(4);
writer.Write(type.Value.X);
writer.Write(type.Value.Y);
writer.Write(type.Value.Z);
writer.Write(type.Value.W);
}
public void Accept(DDateTime type, ref MessagePackWriter writer)
{
writer.Write(type.UnixTimeOfCurrentAssembly);
}
public void Accept(DBean type, ref MessagePackWriter writer)
{
var implType = type.ImplType;
var hierarchyFields = implType.HierarchyFields;
int exportCount = 0;
{
if (type.Type.IsAbstractType)
{
exportCount++;
}
int idx = 0;
foreach (var field in type.Fields)
{
var defField = (DefField)hierarchyFields[idx++];
if (field == null || !defField.NeedExport)
{
continue;
}
++exportCount;
}
}
writer.WriteMapHeader(exportCount);
if (type.Type.IsAbstractType)
{
writer.Write(DefBean.JSON_TYPE_NAME_KEY);
writer.Write(DataUtil.GetImplTypeName(type));
}
int index = 0;
foreach (var field in type.Fields)
{
var defField = (DefField)hierarchyFields[index++];
if (field == null || !defField.NeedExport)
{
continue;
}
writer.Write(defField.Name);
Apply(field, ref writer);
}
}
public void Accept(DArray type, ref MessagePackWriter writer)
{
writer.WriteArrayHeader(type.Datas.Count);
foreach (var d in type.Datas)
{
Apply(d, ref writer);
}
}
public void Accept(DList type, ref MessagePackWriter writer)
{
writer.WriteArrayHeader(type.Datas.Count);
foreach (var d in type.Datas)
{
Apply(d, ref writer);
}
}
public void Accept(DSet type, ref MessagePackWriter writer)
{
writer.WriteArrayHeader(type.Datas.Count);
foreach (var d in type.Datas)
{
Apply(d, ref writer);
}
}
public void Accept(DMap type, ref MessagePackWriter writer)
{
writer.WriteMapHeader(type.Datas.Count);
foreach (var d in type.Datas)
{
Apply(d.Key, ref writer);
Apply(d.Value, ref writer);
}
}
}
}

View File

@ -0,0 +1,328 @@
using Google.Protobuf;
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.DataSources;
using Luban.Job.Cfg.DataVisitors;
using Luban.Job.Cfg.Defs;
using Luban.Job.Common.Generate;
using Luban.Job.Common.Types;
using Luban.Job.Common.TypeVisitors;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Luban.Job.Cfg.DataExporters
{
class ProtobufBinExportor : IDataActionVisitor<CodedOutputStream>
{
public static ProtobufBinExportor Ins { get; } = new();
public void WriteList(DefTable table, List<Record> datas, MemoryStream x)
{
var cos = new CodedOutputStream(x);
foreach (var d in datas)
{
cos.WriteTag(1, WireFormat.WireType.LengthDelimited);
d.Data.Apply(this, cos);
}
cos.Flush();
}
public void Accept(DBool type, CodedOutputStream x)
{
x.WriteBool(type.Value);
}
public void Accept(DByte type, CodedOutputStream x)
{
x.WriteInt32(type.Value);
}
public void Accept(DShort type, CodedOutputStream x)
{
x.WriteInt32(type.Value);
}
public void Accept(DFshort type, CodedOutputStream x)
{
x.WriteInt32(type.Value);
}
public void Accept(DInt type, CodedOutputStream x)
{
x.WriteInt32(type.Value);
}
public void Accept(DFint type, CodedOutputStream x)
{
x.WriteSFixed32(type.Value);
}
public void Accept(DLong type, CodedOutputStream x)
{
x.WriteInt64(type.Value);
}
public void Accept(DFlong type, CodedOutputStream x)
{
x.WriteSFixed64(type.Value);
}
public void Accept(DFloat type, CodedOutputStream x)
{
x.WriteFloat(type.Value);
}
public void Accept(DDouble type, CodedOutputStream x)
{
x.WriteDouble(type.Value);
}
public void Accept(DEnum type, CodedOutputStream x)
{
x.WriteInt32(type.Value);
}
public void Accept(DDateTime type, CodedOutputStream x)
{
x.WriteInt64(type.UnixTimeOfCurrentAssembly);
}
public void Accept(DString type, CodedOutputStream x)
{
x.WriteString(type.Value);
}
public void Accept(DBytes type, CodedOutputStream x)
{
x.WriteBytes(ByteString.CopyFrom(type.Value));
}
public void Accept(DText type, CodedOutputStream x)
{
// 此处与 binary格式不同. binary格式还包含了key
// 意味pb格式是无法支持动态本土化的。
x.WriteString(type.TextOfCurrentAssembly);
}
private MemoryStream AllocMemoryStream()
{
// TODO 优化
return new MemoryStream();
}
private void FreeMemoryStream(MemoryStream cos)
{
cos.Seek(0, SeekOrigin.Begin);
}
public void Accept(DVector2 type, CodedOutputStream x)
{
var ms = AllocMemoryStream();
var temp = new CodedOutputStream(ms);
temp.WriteTag(1, WireFormat.WireType.Fixed32);
temp.WriteFloat(type.Value.X);
temp.WriteTag(2, WireFormat.WireType.Fixed32);
temp.WriteFloat(type.Value.Y);
temp.Flush();
ms.Seek(0, SeekOrigin.Begin);
x.WriteBytes(ByteString.FromStream(ms));
FreeMemoryStream(ms);
}
public void Accept(DVector3 type, CodedOutputStream x)
{
var ms = AllocMemoryStream();
var temp = new CodedOutputStream(ms);
temp.WriteTag(1, WireFormat.WireType.Fixed32);
temp.WriteFloat(type.Value.X);
temp.WriteTag(2, WireFormat.WireType.Fixed32);
temp.WriteFloat(type.Value.Y);
temp.WriteTag(3, WireFormat.WireType.Fixed32);
temp.WriteFloat(type.Value.Z);
temp.Flush();
ms.Seek(0, SeekOrigin.Begin);
x.WriteBytes(ByteString.FromStream(ms));
FreeMemoryStream(ms);
}
public void Accept(DVector4 type, CodedOutputStream x)
{
var ms = AllocMemoryStream();
var temp = new CodedOutputStream(ms);
temp.WriteTag(1, WireFormat.WireType.Fixed32);
temp.WriteFloat(type.Value.X);
temp.WriteTag(2, WireFormat.WireType.Fixed32);
temp.WriteFloat(type.Value.Y);
temp.WriteTag(3, WireFormat.WireType.Fixed32);
temp.WriteFloat(type.Value.Z);
temp.WriteTag(4, WireFormat.WireType.Fixed32);
temp.WriteFloat(type.Value.W);
temp.Flush();
ms.Seek(0, SeekOrigin.Begin);
x.WriteBytes(ByteString.FromStream(ms));
FreeMemoryStream(ms);
}
private void WriteRawMessageWithoutLength(DBean type, CodedOutputStream temp)
{
//var ms = AllocMemoryStream();
//var temp = new CodedOutputStream(ms);
//if (bean.IsAbstractType)
//{
// temp.WriteTag(type.ImplType.AutoId, WireFormat.WireType.LengthDelimited);
//}
var defFields = type.ImplType.HierarchyFields;
int index = 0;
foreach (var field in type.Fields)
{
var defField = (DefField)defFields[index++];
if (!defField.NeedExport)
{
continue;
}
var fieldType = defField.CType;
if (field == null)
{
continue;
}
switch (field)
{
case DArray arr:
{
WriteList(fieldType.ElementType, defField.AutoId, arr.Datas, temp);
break;
}
case DList list:
{
WriteList(fieldType.ElementType, defField.AutoId, list.Datas, temp);
break;
}
case DSet set:
{
WriteList(fieldType.ElementType, defField.AutoId, set.Datas, temp);
break;
}
case DMap map:
{
WriteMap(map, defField.AutoId, temp);
break;
}
default:
{
temp.WriteTag(defField.AutoId, defField.CType.Apply(ProtobufWireTypeVisitor.Ins));
field.Apply(this, temp);
break;
}
}
}
//temp.Flush();
//ms.Seek(0, SeekOrigin.Begin);
//var bs = ByteString.FromStream(ms);
//x.WriteBytes(bs);
//FreeMemoryStream(ms);
}
private void EnterScope(CodedOutputStream x, Action<CodedOutputStream> action)
{
var ms = AllocMemoryStream();
var temp = new CodedOutputStream(ms);
action(temp);
temp.Flush();
ms.Seek(0, SeekOrigin.Begin);
var bs = ByteString.FromStream(ms);
x.WriteBytes(bs);
FreeMemoryStream(ms);
}
public void Accept(DBean type, CodedOutputStream x)
{
EnterScope(x, cos =>
{
var bean = type.Type;
if (bean.IsAbstractType)
{
cos.WriteTag(type.ImplType.AutoId, WireFormat.WireType.LengthDelimited);
EnterScope(cos, cos2 => WriteRawMessageWithoutLength(type, cos2));
}
else
{
WriteRawMessageWithoutLength(type, cos);
}
});
}
private void WriteList(TType elementType, int fieldId, List<DType> datas, CodedOutputStream x)
{
if (elementType.Apply(IsProtobufPackedType.Ins))
{
x.WriteTag(fieldId, WireFormat.WireType.LengthDelimited);
var ms = AllocMemoryStream();
var temp = new CodedOutputStream(ms);
foreach (var data in datas)
{
data.Apply(this, temp);
}
temp.Flush();
ms.Seek(0, SeekOrigin.Begin);
x.WriteBytes(ByteString.FromStream(ms));
FreeMemoryStream(ms);
}
else
{
var eleWireType = elementType.Apply(ProtobufWireTypeVisitor.Ins);
foreach (var data in datas)
{
x.WriteTag(fieldId, eleWireType);
data.Apply(this, x);
}
}
}
public void Accept(DArray type, CodedOutputStream x)
{
throw new NotImplementedException();
}
public void Accept(DList type, CodedOutputStream x)
{
throw new NotImplementedException();
}
public void Accept(DSet type, CodedOutputStream x)
{
throw new NotImplementedException();
}
private void WriteMap(DMap type, int fieldId, CodedOutputStream x)
{
var keyType = type.Type.KeyType;
var valueType = type.Type.ValueType;
var ms = AllocMemoryStream();
foreach (var e in type.Datas)
{
x.WriteTag(fieldId, WireFormat.WireType.LengthDelimited);
ms.Seek(0, SeekOrigin.Begin);
var temp = new CodedOutputStream(ms);
temp.WriteTag(1, keyType.Apply(ProtobufWireTypeVisitor.Ins));
e.Key.Apply(this, temp);
temp.WriteTag(2, valueType.Apply(ProtobufWireTypeVisitor.Ins));
e.Value.Apply(this, temp);
temp.Flush();
ms.Seek(0, SeekOrigin.Begin);
x.WriteBytes(ByteString.FromStream(ms));
}
FreeMemoryStream(ms);
}
public void Accept(DMap type, CodedOutputStream x)
{
throw new NotSupportedException();
}
}
}

View File

@ -0,0 +1,88 @@
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.DataSources;
using Luban.Job.Cfg.DataVisitors;
using Luban.Job.Cfg.Defs;
using Luban.Job.Common.Types;
using Luban.Job.Common.TypeVisitors;
using System;
using System.Collections.Generic;
using System.Text.Json;
namespace Luban.Job.Cfg.DataExporters
{
class ProtobufJsonExportor : JsonExportor
{
public static new FlatBuffersJsonExportor Ins { get; } = new();
public void WriteAsTable(List<Record> datas, Utf8JsonWriter x)
{
x.WriteStartObject();
// 如果修改了这个名字请同时修改table.tpl
x.WritePropertyName("data_list");
x.WriteStartArray();
foreach (var d in datas)
{
d.Data.Apply(this, x);
}
x.WriteEndArray();
x.WriteEndObject();
}
public override void Accept(DText type, Utf8JsonWriter x)
{
// 不支持本地化。只能简单起见这么做了
//x.WriteStartObject();
//x.WritePropertyName(DText.KEY_NAME);
//x.WriteStringValue(type.Key);
//x.WritePropertyName(DText.TEXT_NAME);
x.WriteStringValue(type.TextOfCurrentAssembly);
//x.WriteEndObject();
}
public override void Accept(DBean type, Utf8JsonWriter x)
{
x.WriteStartObject();
if (type.Type.IsAbstractType)
{
// protobuf oneof 用 @type来识别类型
x.WritePropertyName("@type");
x.WriteStringValue(TBean.Create(false, type.ImplType, null).Apply(ProtobufTypeNameVisitor.Ins));
}
var defFields = type.ImplType.HierarchyFields;
int index = 0;
foreach (var d in type.Fields)
{
var defField = (DefField)defFields[index++];
// 特殊处理 bean 多态类型
// 另外,不生成 xxx:null 这样
if (d == null || !defField.NeedExport)
{
//x.WriteNullValue();
}
else
{
x.WritePropertyName(defField.Name);
d.Apply(this, x);
}
}
x.WriteEndObject();
}
public override void Accept(DMap type, Utf8JsonWriter x)
{
x.WriteStartArray();
foreach (var d in type.Datas)
{
x.WriteStartArray();
x.WriteStringValue(d.Key.Apply(ToJsonLiteralVisitor.Ins));
d.Value.Apply(this, x);
x.WriteEndArray();
}
x.WriteEndArray();
}
}
}

View File

@ -104,8 +104,8 @@ namespace Luban.Job.Cfg.DataExporters
if (type.Type.IsAbstractType)
{
x.WritePropertyName(DefBean.TYPE_NAME_KEY);
x.WriteStringValue(type.ImplType.Name);
x.WritePropertyName(DefBean.JSON_TYPE_NAME_KEY);
x.WriteStringValue(DataUtil.GetImplTypeName(type));
}
var defFields = type.ImplType.HierarchyFields;

View File

@ -0,0 +1,202 @@
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.DataSources;
using Luban.Job.Cfg.DataVisitors;
using Luban.Job.Cfg.Defs;
using Luban.Job.Cfg.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace Luban.Job.Cfg.DataExporters
{
public class XmlExportor : IDataActionVisitor<XmlWriter>
{
public static XmlExportor Ins { get; } = new XmlExportor();
public void WriteAsArray(List<Record> datas, XmlWriter w)
{
w.WriteStartDocument();
w.WriteStartElement("table");
foreach (var d in datas)
{
w.WriteStartElement("record");
d.Data.Apply(this, w);
w.WriteEndElement();
}
w.WriteEndElement();
w.WriteEndDocument();
}
public void Accept(DBool type, XmlWriter w)
{
w.WriteValue(type.Value);
}
public void Accept(DByte type, XmlWriter w)
{
w.WriteValue(type.Value);
}
public void Accept(DShort type, XmlWriter w)
{
w.WriteValue(type.Value);
}
public void Accept(DFshort type, XmlWriter w)
{
w.WriteValue(type.Value);
}
public void Accept(DInt type, XmlWriter w)
{
w.WriteValue(type.Value);
}
public void Accept(DFint type, XmlWriter w)
{
w.WriteValue(type.Value);
}
public void Accept(DLong type, XmlWriter w)
{
w.WriteValue(type.Value);
}
public void Accept(DFlong type, XmlWriter w)
{
w.WriteValue(type.Value);
}
public void Accept(DFloat type, XmlWriter w)
{
w.WriteValue(type.Value);
}
public void Accept(DDouble type, XmlWriter w)
{
w.WriteValue(type.Value);
}
public void Accept(DEnum type, XmlWriter w)
{
w.WriteValue(type.Value);
}
public void Accept(DString type, XmlWriter w)
{
w.WriteValue(type.Value);
}
public void Accept(DText type, XmlWriter w)
{
w.WriteElementString(DText.KEY_NAME, type.Key);
w.WriteElementString(DText.TEXT_NAME, type.TextOfCurrentAssembly);
}
public void Accept(DBytes type, XmlWriter w)
{
throw new NotSupportedException();
}
public void Accept(DVector2 type, XmlWriter w)
{
Vector2 v = type.Value;
w.WriteElementString("x", v.X.ToString());
w.WriteElementString("y", v.Y.ToString());
}
public void Accept(DVector3 type, XmlWriter w)
{
Vector3 v = type.Value;
w.WriteElementString("x", v.X.ToString());
w.WriteElementString("y", v.Y.ToString());
w.WriteElementString("z", v.Z.ToString());
}
public void Accept(DVector4 type, XmlWriter w)
{
Vector4 v = type.Value;
w.WriteElementString("x", v.X.ToString());
w.WriteElementString("y", v.Y.ToString());
w.WriteElementString("z", v.Z.ToString());
w.WriteElementString("w", v.W.ToString());
}
public void Accept(DDateTime type, XmlWriter w)
{
w.WriteValue(type.UnixTimeOfCurrentAssembly);
}
public void Accept(DBean type, XmlWriter w)
{
if (type.Type.IsAbstractType)
{
w.WriteAttributeString(DefBean.XML_TYPE_NAME_KEY, DataUtil.GetImplTypeName(type));
}
var defFields = type.ImplType.HierarchyFields;
int index = 0;
foreach (var d in type.Fields)
{
var defField = (DefField)defFields[index++];
// 特殊处理 bean 多态类型
// 另外,不生成 xxx:null 这样
if (d == null || !defField.NeedExport)
{
//x.WriteNullValue();
}
else
{
w.WriteStartElement(defField.Name);
d.Apply(this, w);
w.WriteEndElement();
}
}
}
private void WriteList(List<DType> datas, XmlWriter w)
{
foreach (var d in datas)
{
w.WriteStartElement("ele");
d.Apply(this, w);
w.WriteEndElement();
}
}
public void Accept(DArray type, XmlWriter w)
{
WriteList(type.Datas, w);
}
public void Accept(DList type, XmlWriter w)
{
WriteList(type.Datas, w);
}
public void Accept(DSet type, XmlWriter w)
{
WriteList(type.Datas, w);
}
public void Accept(DMap type, XmlWriter w)
{
foreach (var (k,v) in type.Datas)
{
w.WriteStartElement("ele");
w.WriteStartElement("key");
k.Apply(this, w);
w.WriteEndElement();
w.WriteStartElement("value");
v.Apply(this, w);
w.WriteEndElement();
w.WriteEndElement();
}
}
}
}

View File

@ -0,0 +1,212 @@
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.DataSources;
using Luban.Job.Cfg.DataVisitors;
using Luban.Job.Cfg.Defs;
using Luban.Job.Cfg.Utils;
using System;
using System.Collections.Generic;
using YamlDotNet.Core;
using YamlDotNet.RepresentationModel;
using YamlDotNet.Serialization;
namespace Luban.Job.Cfg.DataExporters
{
class YamlExportor : IDataFuncVisitor<YamlNode>
{
public static YamlExportor Ins { get; } = new YamlExportor();
public YamlNode WriteAsArray(List<Record> datas)
{
var seqNode = new YamlSequenceNode();
foreach (var d in datas)
{
seqNode.Add(d.Data.Apply(this));
}
return seqNode;
}
private static YamlScalarNode ToPlainNode(string x)
{
return new YamlScalarNode(x) { Style = ScalarStyle.Plain };
}
private static YamlScalarNode ToText(string x)
{
return new YamlScalarNode(x) { Style = ScalarStyle.SingleQuoted };
}
public YamlNode Accept(DBool type)
{
return ToPlainNode(type.Value ? "true" : "false");
}
public YamlNode Accept(DByte type)
{
return ToPlainNode(type.Value.ToString());
}
public YamlNode Accept(DShort type)
{
return ToPlainNode(type.Value.ToString());
}
public YamlNode Accept(DFshort type)
{
return ToPlainNode(type.Value.ToString());
}
public YamlNode Accept(DInt type)
{
return ToPlainNode(type.Value.ToString());
}
public YamlNode Accept(DFint type)
{
return ToPlainNode(type.Value.ToString());
}
public YamlNode Accept(DLong type)
{
return ToPlainNode(type.Value.ToString());
}
public YamlNode Accept(DFlong type)
{
return ToPlainNode(type.Value.ToString());
}
public YamlNode Accept(DFloat type)
{
return ToPlainNode(type.Value.ToString());
}
public YamlNode Accept(DDouble type)
{
return ToPlainNode(type.Value.ToString());
}
public YamlNode Accept(DEnum type)
{
return ToPlainNode(type.Value.ToString());
}
public YamlNode Accept(DString type)
{
return ToText(type.Value);
}
public YamlNode Accept(DBytes type)
{
throw new NotSupportedException();
}
public YamlNode Accept(DText type)
{
var m = new YamlMappingNode();
m.Add(DText.KEY_NAME, ToText(type.Key));
m.Add(DText.TEXT_NAME, ToText(type.TextOfCurrentAssembly));
return m;
}
public YamlNode Accept(DBean type)
{
var m = new YamlMappingNode();
if (type.Type.IsAbstractType)
{
m.Add(DefBean.JSON_TYPE_NAME_KEY, ToText(DataUtil.GetImplTypeName(type)));
}
var defFields = type.ImplType.HierarchyFields;
int index = 0;
foreach (var d in type.Fields)
{
var defField = (DefField)defFields[index++];
// 特殊处理 bean 多态类型
// 另外,不生成 xxx:null 这样
if (d == null || !defField.NeedExport)
{
//x.WriteNullValue();
}
else
{
m.Add(defField.Name, d.Apply(this));
}
}
return m;
}
public YamlSequenceNode ToSeqNode(List<DType> datas)
{
var seqNode = new YamlSequenceNode();
foreach (var d in datas)
{
seqNode.Add(d.Apply(this));
}
return seqNode;
}
public YamlNode Accept(DArray type)
{
return ToSeqNode(type.Datas);
}
public YamlNode Accept(DList type)
{
return ToSeqNode(type.Datas);
}
public YamlNode Accept(DSet type)
{
return ToSeqNode(type.Datas);
}
public YamlNode Accept(DMap type)
{
var seqNode = new YamlSequenceNode();
foreach (var d in type.Datas)
{
var e = new YamlSequenceNode();
e.Add(d.Key.Apply(this));
e.Add(d.Value.Apply(this));
seqNode.Add(e);
}
return seqNode;
}
public YamlNode Accept(DVector2 type)
{
var m = new YamlMappingNode();
m.Add("x", type.Value.X.ToString());
m.Add("y", type.Value.Y.ToString());
return m;
}
public YamlNode Accept(DVector3 type)
{
var m = new YamlMappingNode();
m.Add("x", type.Value.X.ToString());
m.Add("y", type.Value.Y.ToString());
m.Add("z", type.Value.Z.ToString());
return m;
}
public YamlNode Accept(DVector4 type)
{
var m = new YamlMappingNode();
m.Add("x", type.Value.X.ToString());
m.Add("y", type.Value.Y.ToString());
m.Add("z", type.Value.Z.ToString());
m.Add("w", type.Value.W.ToString());
return m;
}
public YamlNode Accept(DDateTime type)
{
return ToPlainNode(type.UnixTimeOfCurrentAssembly.ToString());
}
}
}

View File

@ -1,26 +0,0 @@
using Luban.Job.Cfg.Datas;
using Luban.Job.Common.Types;
using System;
using System.Collections.Generic;
using System.IO;
namespace Luban.Job.Cfg.DataSources.Binary
{
class BinaryDataSource : AbstractDataSource
{
public override void Load(string rawUrl, string sheetName, Stream stream)
{
throw new NotImplementedException();
}
public override List<Record> ReadMulti(TBean type)
{
throw new NotImplementedException();
}
public override Record ReadOne(TBean type)
{
throw new NotImplementedException();
}
}
}

View File

@ -1,5 +1,8 @@
using Luban.Job.Cfg.DataCreators;
using Luban.Job.Cfg.DataSources.Excel;
using Luban.Job.Cfg.Defs;
using System;
using System.Collections.Generic;
using System.IO;
namespace Luban.Job.Cfg.DataSources
@ -15,29 +18,58 @@ namespace Luban.Job.Cfg.DataSources
".lua",
".json",
".yml",
".bin",
".asset",
};
public static AbstractDataSource Create(string url, string sheetName, Stream stream)
private static string GetSheetParserMode(Dictionary<string, string> options)
{
if (options != null && options.TryGetValue("parser_mode", out var modeStr))
{
return modeStr;
}
//options = DefAssembly.LocalAssebmly.Options;
//if (options != null && options.TryGetValue("sheet.parser_mode", out modeStr))
//{
// return modeStr;
//}
return "";
}
private static bool IsColumnMode(string mode)
{
if (string.IsNullOrEmpty(mode))
{
return true;
}
switch(mode.ToLowerInvariant())
{
case "column": return true;
case "stream": return false;
default: throw new Exception($"unknown parser_mode:{mode}");
}
}
public static AbstractDataSource Create(string url, string sheetName, Dictionary<string, string> options, Stream stream)
{
try
{
#if !LUBAN_LITE
string ext = url.Contains('.') ? Path.GetExtension(url)?[1..] : url;
#else
string ext = url.Contains(".") ? Path.GetExtension(url)?.Substring(1) : url;
#endif
AbstractDataSource source;
switch (ext)
{
case "csv":
case "xls":
case "xlsx": source = new Excel.ExcelDataSource(); break;
case "xlsx":
case "xlsm":
{
source = IsColumnMode(GetSheetParserMode(options)) ? new ExcelRowColumnDataSource() : new ExcelStreamDataSource();
break;
}
case "xml": source = new Xml.XmlDataSource(); break;
case "lua": source = new Lua.LuaDataSource(); break;
case "json": source = new Json.JsonDataSource(); break;
case "bin": source = new Binary.BinaryDataSource(); break;
case "yml": source = new Yaml.YamlDataSource(); break;
case "asset": source = new UnityAsset.UnityAssetDataSource(); break;
default: throw new Exception($"不支持的文件类型:{url}");
}
source.Load(url, sheetName, stream);

View File

@ -10,11 +10,11 @@ using System.IO;
namespace Luban.Job.Cfg.DataSources.Excel
{
class ExcelDataSource : AbstractDataSource
class ExcelRowColumnDataSource : AbstractDataSource
{
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
private readonly List<Sheet> _sheets = new List<Sheet>();
private readonly List<RowColumnSheet> _sheets = new List<RowColumnSheet>();
public override void Load(string rawUrl, string sheetName, Stream stream)
@ -25,7 +25,7 @@ namespace Luban.Job.Cfg.DataSources.Excel
foreach (RawSheet rawSheet in SheetLoadUtil.LoadRawSheets(rawUrl, sheetName, stream))
{
var sheet = new Sheet(rawUrl, sheetName);
var sheet = new RowColumnSheet(rawUrl, sheetName);
sheet.Load(rawSheet);
_sheets.Add(sheet);
}
@ -40,7 +40,7 @@ namespace Luban.Job.Cfg.DataSources.Excel
{
foreach (RawSheet rawSheet in rawSheets)
{
var sheet = new Sheet("__intern__", rawSheet.TableName);
var sheet = new RowColumnSheet("__intern__", rawSheet.TableName);
sheet.Load(rawSheet);
_sheets.Add(sheet);
}

View File

@ -265,10 +265,10 @@ namespace Luban.Job.Cfg.DataSources.Excel
int oldIndex = _curIndex;
while (_curIndex <= _toIndex)
{
var value = _datas[_curIndex++].Value?.ToString();
var value = _datas[_curIndex++].Value;
if (!IsSkip(value))
{
if (value == END_OF_LIST)
if (value is string s && s == END_OF_LIST)
{
LastReadIndex = _curIndex - 1;
return true;
@ -284,5 +284,23 @@ namespace Luban.Job.Cfg.DataSources.Excel
_curIndex = oldIndex;
return true;
}
internal ExcelStream CreateAutoSepStream(string simpleContainerSep)
{
int startIndex = _curIndex;
while (_curIndex <= _toIndex)
{
var value = _datas[_curIndex++].Value;
if (!IsSkip(value))
{
if (value is string s && s == END_OF_LIST)
{
break;
}
}
}
LastReadIndex = _curIndex - 1;
return new ExcelStream(_datas, startIndex, LastReadIndex, simpleContainerSep, "");
}
}
}

View File

@ -0,0 +1,76 @@
using ExcelDataReader;
using Luban.Job.Cfg.DataCreators;
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.Utils;
using Luban.Job.Common.Types;
using System;
using System.Collections.Generic;
using System.IO;
namespace Luban.Job.Cfg.DataSources.Excel
{
class ExcelStreamDataSource : AbstractDataSource
{
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
private readonly List<StreamSheet> _sheets = new List<StreamSheet>();
public override void Load(string rawUrl, string sheetName, Stream stream)
{
s_logger.Trace("{filename} {sheet}", rawUrl, sheetName);
RawUrl = rawUrl;
foreach (RawSheet rawSheet in SheetLoadUtil.LoadRawSheets(rawUrl, sheetName, stream))
{
var sheet = new StreamSheet(rawUrl, sheetName);
sheet.Load(rawSheet);
_sheets.Add(sheet);
}
if (_sheets.Count == 0)
{
throw new Exception($"excel:{rawUrl} 不包含有效的单元薄(有效单元薄的A0单元格必须是##).");
}
}
public RawSheetTableDefInfo LoadTableDefInfo(string rawUrl, string sheetName, Stream stream)
{
return SheetLoadUtil.LoadSheetTableDefInfo(rawUrl, sheetName, stream);
}
public override List<Record> ReadMulti(TBean type)
{
var datas = new List<Record>();
foreach (var sheet in _sheets)
{
try
{
var stream = sheet.Stream;
while(!stream.TryReadEOF())
{
var data = (DBean)type.Apply(ExcelStreamDataCreator.Ins, stream);
datas.Add(new Record(data, sheet.RawUrl, null));
}
}
catch (DataCreateException dce)
{
dce.OriginDataLocation = sheet.RawUrl;
throw;
}
catch (Exception e)
{
throw new Exception($"sheet:{sheet.Name}", e);
}
}
return datas;
}
public override Record ReadOne(TBean type)
{
throw new Exception($"excel不支持单例读取模式");
}
}
}

View File

@ -1,4 +1,5 @@
using System;
using DocumentFormat.OpenXml.EMMA;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -15,6 +16,7 @@ namespace Luban.Job.Cfg.DataSources.Excel
public string Type { get; set; }
public string Desc { get; set; }
public string Groups { get; set; }
}
class RawSheetTableDefInfo

View File

@ -12,7 +12,7 @@ using System.Linq;
namespace Luban.Job.Cfg.DataSources.Excel
{
class Sheet
class RowColumnSheet
{
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
@ -22,7 +22,7 @@ namespace Luban.Job.Cfg.DataSources.Excel
public List<(string Tag, TitleRow Row)> Rows { get; } = new();
public Sheet(string rawUrl, string name)
public RowColumnSheet(string rawUrl, string name)
{
this.RawUrl = rawUrl;
this.Name = name;
@ -42,7 +42,7 @@ namespace Luban.Job.Cfg.DataSources.Excel
{
foreach (var row in cells)
{
if (IsBlankRow(row, title.FromIndex, title.ToIndex))
if (IsBlankRow(row, title))
{
continue;
}
@ -188,6 +188,15 @@ namespace Luban.Job.Cfg.DataSources.Excel
return Rows;
}
public static bool IsBlankRow(List<Cell> row, Title title)
{
if (title.SubTitleList.Count == 0)
{
return IsBlankRow(row, title.FromIndex, title.ToIndex);
}
return title.SubTitleList.All(t => IsBlankRow(row, t));
}
public static bool IsBlankRow(List<Cell> row, int fromIndex, int toIndex)
{
for (int i = Math.Max(1, fromIndex), n = Math.Min(toIndex, row.Count - 1); i <= n; i++)

View File

@ -1,10 +1,12 @@
using ExcelDataReader;
using Luban.Job.Cfg.Defs;
using Luban.Job.Common.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Luban.Job.Cfg.DataSources.Excel
@ -30,9 +32,12 @@ namespace Luban.Job.Cfg.DataSources.Excel
}
}
private static readonly AsyncLocal<string> s_curExcel = new();
public static IEnumerable<RawSheet> LoadRawSheets(string rawUrl, string sheetName, Stream stream)
{
s_logger.Trace("{filename} {sheet}", rawUrl, sheetName);
s_curExcel.Value = rawUrl;
string ext = Path.GetExtension(rawUrl);
using (var reader = ext != ".csv" ? ExcelReaderFactory.CreateReader(stream) : ExcelReaderFactory.CreateCsvReader(stream, new ExcelReaderConfiguration() { FallbackEncoding = DetectCsvEncoding(stream) }))
{
@ -67,11 +72,64 @@ namespace Luban.Job.Cfg.DataSources.Excel
return null;
}
var cells = ParseRawSheetContent(reader, orientRow, false);
ValidateTitles(cells);
var title = ParseTitle(cells, reader.MergeCells, orientRow);
cells.RemoveAll(c => c.Count == 0 || IsHeaderRow(c));
cells.RemoveAll(c => IsNotDataRow(c));
return new RawSheet() { Title = title, TableName = tableName, Cells = cells };
}
private static readonly HashSet<string> s_knownSpecialTags = new HashSet<string>
{
"var",
"+",
"type",
"desc",
"comment",
"column",
"group",
};
private const char s_sep = '#';
private static void ValidateTitles(List<List<Cell>> rows)
{
foreach (var row in rows)
{
if (row.Count == 0)
{
continue;
}
string rowTag = row[0].Value?.ToString()?.ToLower()?.Trim();
if (string.IsNullOrEmpty(rowTag))
{
continue;
}
if (!rowTag.StartsWith("##"))
{
break;
}
var tags = rowTag.Substring(2).Split(s_sep).Where(s => !string.IsNullOrEmpty(s));
foreach (string tag in tags)
{
if (!s_knownSpecialTags.Contains(tag))
{
DefAssembly.LocalAssebmly?.Agent?.Error("文件:'{0}' 行标签:'{1}' 包含未知tag:'{2}',是否有拼写错误?", s_curExcel.Value, rowTag, tag);
}
}
}
}
private static bool IsNotDataRow(List<Cell> row)
{
if (row.Count == 0)
{
return true;
}
var s = row[0].Value?.ToString()?.Trim();
return !string.IsNullOrEmpty(s) && s.StartsWith("##");
}
public static Title ParseTitle(List<List<Cell>> cells, CellRange[] mergeCells, bool orientRow)
{
var rootTitle = new Title()
@ -83,9 +141,13 @@ namespace Luban.Job.Cfg.DataSources.Excel
ToIndex = cells.Select(r => r.Count).Max() - 1
};
if (!TryFindTopTitle(cells, out var topTitleRowIndex))
{
throw new Exception($"没有定义任何有效 标题行");
}
//titleRowNum = GetTitleRowNum(mergeCells, orientRow);
ParseSubTitles(rootTitle, cells, mergeCells, orientRow, 1);
ParseSubTitles(rootTitle, cells, mergeCells, orientRow, topTitleRowIndex + 1);
rootTitle.Init();
@ -96,6 +158,41 @@ namespace Luban.Job.Cfg.DataSources.Excel
return rootTitle;
}
private static bool TryFindTopTitle(List<List<Cell>> cells, out int rowIndex)
{
for (int i = 0; i < cells.Count; i++)
{
var row = cells[i];
if (row.Count == 0)
{
break;
}
string rowTag = row[0].Value?.ToString()?.ToLower() ?? "";
if (!rowTag.StartsWith("##"))
{
break;
}
if (rowTag.Substring(2).IndexOf('&') >= 0)
{
throw new Exception($"excel标题头不再使用'&'作为分割符,请改为'{s_sep}'");
}
var tags = rowTag.Substring(2).Split(s_sep).Select(s => s.Trim()).Where(s => !string.IsNullOrEmpty(s)).ToList();
if (tags.Contains("field") || tags.Contains("var") || tags.Contains("+"))
{
rowIndex = i;
return true;
}
// 出于历史兼容性对第一行特殊处理如果不包含任何tag或者只包含column则也认为是标题行
if (i == 0 && (tags.Count == 0 || (tags.Count == 1 && tags[0] == "column")))
{
rowIndex = i;
return true;
}
}
rowIndex = 0;
return false;
}
private static bool TryFindNextSubFieldRowIndex(List<List<Cell>> cells, int startRowIndex, out int rowIndex)
{
for (int i = startRowIndex; i < cells.Count; i++)
@ -105,8 +202,8 @@ namespace Luban.Job.Cfg.DataSources.Excel
{
break;
}
string rowTag = row[0].Value?.ToString() ?? "";
if (rowTag.StartsWith("##field"))
string rowTag = row[0].Value?.ToString()?.ToLower() ?? "";
if (rowTag == "##field" || rowTag == "##var" || rowTag == "##+")
{
rowIndex = i;
return true;
@ -122,16 +219,16 @@ namespace Luban.Job.Cfg.DataSources.Excel
private static bool IsIgnoreTitle(string title)
{
#if !LUBAN_LITE
return string.IsNullOrEmpty(title) || title.StartsWith('#');
#else
return string.IsNullOrEmpty(title) || title.StartsWith("#");
#endif
}
public static (string Name, Dictionary<string, string> Tags) ParseNameAndMetaAttrs(string nameAndAttrs)
{
var attrs = nameAndAttrs.Split('&');
if (nameAndAttrs.Contains('&'))
{
throw new Exception($"excel标题头不再使用'&'作为分割符,请改为'{s_sep}'");
}
var attrs = nameAndAttrs.Split(s_sep);
string titleName = attrs[0];
var tags = new Dictionary<string, string>();
@ -141,11 +238,21 @@ namespace Luban.Job.Cfg.DataSources.Excel
titleName = titleName.Substring(1);
tags.Add("multi_rows", "1");
}
//if (titleName.EndsWith("*"))
//{
// titleName = titleName.Substring(0, titleName.Length - 1);
// tags.Add("multi_rows", "1");
//}
if (titleName.StartsWith("!"))
{
titleName = titleName.Substring(1);
tags.Add("non_empty", "1");
}
//if (titleName.EndsWith("!"))
//{
// titleName = titleName.Substring(0, titleName.Length - 1);
// tags.Add("non_empty", "1");
//}
foreach (var attrPair in attrs.Skip(1))
{
var pairs = attrPair.Split('=');
@ -153,7 +260,7 @@ namespace Luban.Job.Cfg.DataSources.Excel
{
throw new Exception($"invalid title: {nameAndAttrs}");
}
tags.Add(pairs[0], pairs[1]);
tags.Add(pairs[0].Trim(), pairs[1].Trim());
}
return (titleName, tags);
}
@ -184,16 +291,16 @@ namespace Luban.Job.Cfg.DataSources.Excel
}
else
{
if (mergeCell.FromColumn == rowIndex && mergeCell.FromRow - 1 >= title.FromIndex && mergeCell.FromRow - 1 <= title.ToIndex)
if (mergeCell.FromColumn == rowIndex && mergeCell.FromRow >= title.FromIndex && mergeCell.FromRow <= title.ToIndex)
{
// 标题 行
var nameAndAttrs = titleRow[mergeCell.FromRow - 1].Value?.ToString()?.Trim();
var nameAndAttrs = titleRow[mergeCell.FromRow].Value?.ToString()?.Trim();
if (IsIgnoreTitle(nameAndAttrs))
{
continue;
}
var (titleName, tags) = ParseNameAndMetaAttrs(nameAndAttrs);
subTitle = new Title() { Name = titleName, Tags = tags, FromIndex = mergeCell.FromRow - 1, ToIndex = mergeCell.ToRow - 1 };
subTitle = new Title() { Name = titleName, Tags = tags, FromIndex = mergeCell.FromRow, ToIndex = mergeCell.ToRow };
}
}
if (subTitle == null)
@ -219,18 +326,48 @@ namespace Luban.Job.Cfg.DataSources.Excel
}
var (titleName, tags) = ParseNameAndMetaAttrs(nameAndAttrs);
if (title.SubTitles.TryGetValue(titleName, out var subTitle))
Title subTitle;
// [field,,,, field] 形成多列字段
if (titleName.StartsWith('['))
{
if (subTitle.FromIndex != i)
int startIndex = i;
titleName = titleName.Substring(1);
bool findEndPair = false;
for (++i; i <= title.ToIndex; i++)
{
throw new Exception($"列:{titleName} 重复");
var endNamePair = titleRow[i].Value?.ToString()?.Trim();
if (string.IsNullOrEmpty(endNamePair))
{
continue;
}
if (!endNamePair.EndsWith(']') || endNamePair[0..^1] != titleName)
{
throw new Exception($"列:'[{titleName}' 后第一个有效列必须为匹配 '{titleName}]',却发现:'{endNamePair}'");
}
findEndPair = true;
break;
}
else
if (!findEndPair)
{
continue;
throw new Exception($"列:'[{titleName}' 未找到结束匹配列 '{titleName}]'");
}
subTitle = new Title() { Name = titleName, Tags = tags, FromIndex = startIndex, ToIndex = i };
}
else
{
if (title.SubTitles.TryGetValue(titleName, out subTitle))
{
if (subTitle.FromIndex != i)
{
throw new Exception($"列:{titleName} 重复");
}
else
{
continue;
}
}
subTitle = new Title() { Name = titleName, Tags = tags, FromIndex = i, ToIndex = i };
}
subTitle = new Title() { Name = titleName, Tags = tags, FromIndex = i, ToIndex = i };
if (excelRowIndex < cells.Count && TryFindNextSubFieldRowIndex(cells, excelRowIndex, out int nextRowIndex))
{
ParseSubTitles(subTitle, cells, mergeCells, orientRow, nextRowIndex + 1);
@ -249,8 +386,11 @@ namespace Luban.Job.Cfg.DataSources.Excel
{
return false;
}
foreach (var attr in metaStr.Substring(2).Split('&'))
if (metaStr.Substring(2).Contains('&'))
{
throw new Exception($"excel标题头不再使用'&'作为分割符,请改为'{s_sep}'");
}
foreach (var attr in metaStr.Substring(2).Split(s_sep))
{
if (string.IsNullOrWhiteSpace(attr))
{
@ -262,6 +402,15 @@ namespace Luban.Job.Cfg.DataSources.Excel
string value = sepIndex >= 0 ? attr.Substring(sepIndex + 1) : "";
switch (key)
{
case "field":
case "+":
case "var":
case "comment":
case "desc":
case "type":
{
break;
}
case "row":
{
orientRow = true;
@ -279,7 +428,7 @@ namespace Luban.Job.Cfg.DataSources.Excel
}
default:
{
throw new Exception($"非法单元薄 meta 属性定义 {attr}, 合法属性有: row,column,table=<tableName>");
throw new Exception($"非法单元薄 meta 属性定义 {attr}, 合法属性有: +,var,row,column,table=<tableName>");
}
}
}
@ -302,6 +451,10 @@ namespace Luban.Job.Cfg.DataSources.Excel
{
return IsRowTagEqual(row, "##type");
}
private static bool IsGroupRow(List<Cell> row)
{
return IsRowTagEqual(row, "##group");
}
private static bool IsHeaderRow(List<Cell> row)
{
@ -319,7 +472,7 @@ namespace Luban.Job.Cfg.DataSources.Excel
{
return false;
}
var s = row[0].Value?.ToString()?.Trim();
var s = row[0].Value?.ToString()?.Trim()?.ToLower();
return s == tag;
}
@ -334,7 +487,6 @@ namespace Luban.Job.Cfg.DataSources.Excel
int rowIndex = 0;
do
{
++rowIndex; // 第1行是 meta 标题及数据行从第2行开始
var row = new List<Cell>();
for (int i = 0, n = reader.FieldCount; i < n; i++)
{
@ -345,6 +497,7 @@ namespace Luban.Job.Cfg.DataSources.Excel
{
break;
}
++rowIndex;
} while (reader.Read());
List<List<Cell>> finalRows;
@ -429,7 +582,7 @@ namespace Luban.Job.Cfg.DataSources.Excel
{
descRow = cells.Count > 1 ? cells.Skip(1).FirstOrDefault(row => IsRowTagEqual(row, "##")) : null;
}
List<Cell> groupRow = cells.Find(row => IsGroupRow(row));
var fields = new Dictionary<string, FieldInfo>();
foreach (var subTitle in title.SubTitleList)
{
@ -467,8 +620,9 @@ namespace Luban.Job.Cfg.DataSources.Excel
fields.Add(subTitle.Name, new FieldInfo()
{
Name = subTitle.Name,
Tags = title.Tags,
Tags = subTitle.Tags,
Type = typeRow[subTitle.FromIndex].Value?.ToString() ?? "",
Groups = groupRow?[subTitle.FromIndex].Value?.ToString() ?? "",
Desc = desc,
});
}

View File

@ -0,0 +1,37 @@
using Bright.Collections;
using ExcelDataReader;
using Luban.Job.Cfg.DataCreators;
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.Utils;
using Luban.Job.Common.Types;
using Luban.Job.Common.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Luban.Job.Cfg.DataSources.Excel
{
class StreamSheet
{
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
public string Name { get; }
public string RawUrl { get; }
public ExcelStream Stream { get; private set; }
public StreamSheet(string rawUrl, string name)
{
this.RawUrl = rawUrl;
this.Name = name;
}
public void Load(RawSheet rawSheet)
{
Title title = rawSheet.Title;
Stream = new ExcelStream(rawSheet.Cells, 1, title.ToIndex, "", "");
}
}
}

View File

@ -28,7 +28,7 @@ namespace Luban.Job.Cfg.DataSources.Excel
public string SepOr(string sep)
{
return string.IsNullOrEmpty(Sep) ? sep : Sep;
return string.IsNullOrEmpty(sep) ? Sep : sep;
}
public bool NonEmpty { get; private set; }
@ -45,7 +45,7 @@ namespace Luban.Job.Cfg.DataSources.Excel
{
if (!SubTitles.TryAdd(title.Name, title))
{
throw new Exception($"标题:{title.Name} 重复");
throw new Exception($":{title.Name} 重复");
}
SubTitleList.Add(title);
}
@ -62,13 +62,34 @@ namespace Luban.Job.Cfg.DataSources.Excel
}
}
private static HashSet<string> s_validTags = new HashSet<string>()
{
"sep",
"non_empty",
"multi_rows",
"default",
};
public void Init()
{
SortSubTitles();
Sep = Tags.TryGetValue("sep", out var v) && !string.IsNullOrWhiteSpace(v) ? v : null;
Sep = Tags.TryGetValue("sep", out var sep) ? sep : "";
//if (Tags.ContainsKey("sep"))
//{
// throw new Exception($"字段名现在不支持sep请移到##type行例如'int&sep=;'");
//}
NonEmpty = Tags.TryGetValue("non_empty", out var ne) && ne == "1";
SelfMultiRows = Tags.TryGetValue("multi_rows", out var v2) && (v2 == "1" || v2 == "true");
Default = Tags.TryGetValue("default", out var v3) ? v3 : null;
foreach (var (key, value) in Tags)
{
if (!s_validTags.Contains(key))
{
throw new Exception($"excel标题列:'{Name}' 不支持tag:'{key}',请移到##type行");
}
}
if (SubTitleList.Count > 0)
{
if (Root)

View File

@ -16,14 +16,21 @@ namespace Luban.Job.Cfg.DataSources.Excel
{
get
{
var v = Row[SelfTitle.FromIndex].Value;
if (v == null || (v is string s && string.IsNullOrEmpty(s) && !string.IsNullOrEmpty(SelfTitle.Default)))
if (Row != null)
{
return SelfTitle.Default;
var v = Row[SelfTitle.FromIndex].Value;
if (v == null || (v is string s && string.IsNullOrEmpty(s) && !string.IsNullOrEmpty(SelfTitle.Default)))
{
return SelfTitle.Default;
}
else
{
return v;
}
}
else
{
return v;
throw new Exception($"简单数据类型字段 不支持子列名或者多行");
}
}
}
@ -62,33 +69,41 @@ namespace Luban.Job.Cfg.DataSources.Excel
}
}
public ExcelStream AsStream(string sep)
public bool IsBlank
{
if (string.IsNullOrEmpty(SelfTitle.Sep))
get
{
if (string.IsNullOrEmpty(sep))
if (Row != null)
{
return new ExcelStream(Row, SelfTitle.FromIndex, SelfTitle.ToIndex, "", SelfTitle.Default);
return RowColumnSheet.IsBlankRow(Row, SelfTitle.FromIndex, SelfTitle.ToIndex);
}
else
if (Rows != null)
{
return new ExcelStream(Row, SelfTitle.FromIndex, SelfTitle.ToIndex, sep, SelfTitle.Default);
return RowColumnSheet.IsBlankRow(Rows[0], SelfTitle.FromIndex, SelfTitle.ToIndex);
}
}
else
{
//if (string.IsNullOrEmpty(sep) || sep == SelfTitle.Sep)
//{
// return new ExcelStream(Row, SelfTitle.FromIndex, SelfTitle.ToIndex, sep);
//}
//else
//{
// SelfTitle.Sep 设置覆盖 bean的 sep设置只有这个可能
return new ExcelStream(Row, SelfTitle.FromIndex, SelfTitle.ToIndex, SelfTitle.Sep, SelfTitle.Default);
//}
if (Fields != null)
{
return Fields.Values.All(f => f.IsBlank);
}
if (Elements != null)
{
return Elements.All(e => e.IsBlank);
}
throw new Exception();
}
}
public ExcelStream AsStream(string sep)
{
if (string.IsNullOrEmpty(sep))
{
return new ExcelStream(Row, SelfTitle.FromIndex, SelfTitle.ToIndex, "", SelfTitle.Default);
}
else
{
return new ExcelStream(Row, SelfTitle.FromIndex, SelfTitle.ToIndex, sep, SelfTitle.Default);
}
}
public bool HasSubFields => Fields != null || Elements != null;
@ -147,13 +162,12 @@ namespace Luban.Job.Cfg.DataSources.Excel
public ExcelStream AsMultiRowConcatStream(string sep)
{
sep = string.IsNullOrEmpty(sep) ? SelfTitle.Sep : sep;
return new ExcelStream(Rows, SelfTitle.FromIndex, SelfTitle.ToIndex, sep, SelfTitle.Default);
}
public ExcelStream AsMultiRowConcatElements()
public ExcelStream AsMultiRowConcatElements(string sep)
{
return new ExcelStream(Elements.Select(e => e.Row).ToList(), SelfTitle.FromIndex, SelfTitle.ToIndex, SelfTitle.Sep, SelfTitle.Default);
return new ExcelStream(Elements.Select(e => e.Row).ToList(), SelfTitle.FromIndex, SelfTitle.ToIndex, sep, SelfTitle.Default);
}
}
}

View File

@ -12,23 +12,47 @@ namespace Luban.Job.Cfg.DataSources.Json
{
class JsonDataSource : AbstractDataSource
{
JsonElement _data;
private JsonElement _data;
public override void Load(string rawUrl, string sheetName, Stream stream)
public override void Load(string rawUrl, string sheetOrFieldName, Stream stream)
{
RawUrl = rawUrl;
this._data = JsonDocument.Parse(stream).RootElement;
if (!string.IsNullOrEmpty(sheetOrFieldName))
{
if (sheetOrFieldName.StartsWith("*"))
{
sheetOrFieldName = sheetOrFieldName.Substring(1);
}
if (!string.IsNullOrEmpty(sheetOrFieldName))
{
foreach (var subField in sheetOrFieldName.Split('.'))
{
_data = _data.GetProperty(subField);
}
}
}
}
public override List<Record> ReadMulti(TBean type)
{
throw new NotImplementedException();
var records = new List<Record>();
foreach (var ele in _data.EnumerateArray())
{
Record rec = ReadRecord(ele, type);
if (rec != null)
{
records.Add(rec);
}
}
return records;
}
public override Record ReadOne(TBean type)
private Record ReadRecord(JsonElement ele, TBean type)
{
List<string> tags;
if (_data.TryGetProperty(TAG_KEY, out var tagEle))
if (ele.TryGetProperty(TAG_KEY, out var tagEle))
{
var tagName = tagEle.GetString();
if (DataUtil.IsIgnoreTag(tagName))
@ -42,8 +66,13 @@ namespace Luban.Job.Cfg.DataSources.Json
tags = null;
}
var data = (DBean)type.Apply(JsonDataCreator.Ins, _data, (DefAssembly)type.Bean.AssemblyBase);
var data = (DBean)type.Apply(JsonDataCreator.Ins, ele, (DefAssembly)type.Bean.AssemblyBase);
return new Record(data, RawUrl, tags);
}
public override Record ReadOne(TBean type)
{
return ReadRecord(_data, type);
}
}
}

View File

@ -22,6 +22,21 @@ namespace Luban.Job.Cfg.DataSources.Lua
RawUrl = rawUrl;
_env = LuaManager.CreateEnvironment();
_dataTable = (LuaTable)_env.DoChunk(new StreamReader(stream, Encoding.UTF8), rawUrl)[0];
if (!string.IsNullOrEmpty(sheetName))
{
if (sheetName.StartsWith("*"))
{
sheetName = sheetName.Substring(1);
}
if (!string.IsNullOrEmpty(sheetName))
{
foreach (var subField in sheetName.Split('.'))
{
_dataTable = (LuaTable)_dataTable[subField];
}
}
}
}
public override List<Record> ReadMulti(TBean type)

View File

@ -11,8 +11,6 @@ namespace Luban.Job.Cfg.DataSources
public List<string> Tags { get; }
public int Index { get; set; }
public bool IsNotFiltered(List<string> excludeTags)
{
if (Tags == null)

View File

@ -0,0 +1,89 @@
using Luban.Job.Cfg.DataCreators;
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.Defs;
using Luban.Job.Cfg.Utils;
using Luban.Job.Common.Types;
using System;
using System.Collections.Generic;
using System.IO;
using YamlDotNet.RepresentationModel;
using System.Linq;
namespace Luban.Job.Cfg.DataSources.UnityAsset
{
class UnityAssetDataSource : AbstractDataSource
{
private YamlNode _root;
public override void Load(string rawUrl, string sheetOrFieldName, Stream stream)
{
var ys = new YamlStream();
ys.Load(new StreamReader(stream));
var rootNode = (YamlMappingNode)ys.Documents[0].RootNode;
// unity asset 格式为 包含一个doc的 yaml文件
// doc顶层为map只包含一个字段字段key为类型名。
if (rootNode.Children.Count != 1)
{
throw new Exception($"asset doc 应该只包含一个顶层字段");
}
this._root = rootNode.First().Value;
if (!string.IsNullOrEmpty(sheetOrFieldName))
{
if (sheetOrFieldName.StartsWith("*"))
{
sheetOrFieldName = sheetOrFieldName[1..];
}
if (!string.IsNullOrEmpty(sheetOrFieldName))
{
foreach (var subField in sheetOrFieldName.Split('.'))
{
this._root = _root[new YamlScalarNode(subField)];
}
}
}
}
public override List<Record> ReadMulti(TBean type)
{
var records = new List<Record>();
foreach (var ele in (YamlSequenceNode)_root)
{
var rec = ReadRecord(ele, type);
if (rec != null)
{
records.Add(rec);
}
}
return records;
}
private static readonly YamlScalarNode s_tagNameNode = new(TAG_KEY);
public override Record ReadOne(TBean type)
{
return ReadRecord(_root, type);
}
private Record ReadRecord(YamlNode yamlNode, TBean type)
{
string tagName;
if (((YamlMappingNode)yamlNode).Children.TryGetValue(s_tagNameNode, out var tagNode))
{
tagName = (string)tagNode;
}
else
{
tagName = null;
}
if (DataUtil.IsIgnoreTag(tagName))
{
return null;
}
var data = (DBean)type.Apply(UnityAssetDataCreator.Ins, yamlNode, (DefAssembly)type.Bean.AssemblyBase);
var tags = DataUtil.ParseTags(tagName);
return new Record(data, RawUrl, tags);
}
}
}

View File

@ -12,40 +12,56 @@ namespace Luban.Job.Cfg.DataSources.Yaml
{
class YamlDataSource : AbstractDataSource
{
private YamlMappingNode _root;
public override void Load(string rawUrl, string sheetName, Stream stream)
private YamlNode _root;
public override void Load(string rawUrl, string sheetOrFieldName, Stream stream)
{
var ys = new YamlStream();
ys.Load(new StreamReader(stream));
var rootNode = (YamlMappingNode)ys.Documents[0].RootNode;
if (string.IsNullOrEmpty(sheetName))
var rootNode = ys.Documents[0].RootNode;
this._root = rootNode;
if (!string.IsNullOrEmpty(sheetOrFieldName))
{
this._root = rootNode;
}
else
{
if (rootNode.Children.TryGetValue(new YamlScalarNode(sheetName), out var childNode))
if (sheetOrFieldName.StartsWith("*"))
{
this._root = (YamlMappingNode)childNode;
sheetOrFieldName = sheetOrFieldName.Substring(1);
}
else
if (!string.IsNullOrEmpty(sheetOrFieldName))
{
throw new Exception($"yaml文件:{RawUrl} 不包含子字段:{sheetName}");
foreach (var subField in sheetOrFieldName.Split('.'))
{
this._root = _root[new YamlScalarNode(subField)];
}
}
}
}
public override List<Record> ReadMulti(TBean type)
{
throw new NotImplementedException();
var records = new List<Record>();
foreach (var ele in (YamlSequenceNode)_root)
{
var rec = ReadRecord(ele, type);
if (rec != null)
{
records.Add(rec);
}
}
return records;
}
private readonly static YamlScalarNode s_tagNameNode = new(TAG_KEY);
private static readonly YamlScalarNode s_tagNameNode = new(TAG_KEY);
public override Record ReadOne(TBean type)
{
return ReadRecord(_root, type);
}
private Record ReadRecord(YamlNode yamlNode, TBean type)
{
string tagName;
if (_root.Children.TryGetValue(s_tagNameNode, out var tagNode))
if (((YamlMappingNode)yamlNode).Children.TryGetValue(s_tagNameNode, out var tagNode))
{
tagName = (string)tagNode;
}
@ -57,7 +73,7 @@ namespace Luban.Job.Cfg.DataSources.Yaml
{
return null;
}
var data = (DBean)type.Apply(YamlDataCreator.Ins, _root, (DefAssembly)type.Bean.AssemblyBase);
var data = (DBean)type.Apply(YamlDataCreator.Ins, yamlNode, (DefAssembly)type.Bean.AssemblyBase);
var tags = DataUtil.ParseTags(tagName);
return new Record(data, RawUrl, tags);
}

View File

@ -28,9 +28,17 @@ namespace Luban.Job.Cfg.DataVisitors
void Accept(DString type, T x);
void Accept(DText type, T x);
void Accept(DBytes type, T x);
void Accept(DText type, T x);
void Accept(DVector2 type, T x);
void Accept(DVector3 type, T x);
void Accept(DVector4 type, T x);
void Accept(DDateTime type, T x);
void Accept(DBean type, T x);
@ -41,14 +49,6 @@ namespace Luban.Job.Cfg.DataVisitors
void Accept(DSet type, T x);
void Accept(DMap type, T x);
void Accept(DVector2 type, T x);
void Accept(DVector3 type, T x);
void Accept(DVector4 type, T x);
void Accept(DDateTime type, T x);
}
public interface IDataActionVisitor<T1, T2>
@ -75,11 +75,19 @@ namespace Luban.Job.Cfg.DataVisitors
void Accept(DEnum type, T1 x, T2 y);
void Accept(DDateTime type, T1 x, T2 y);
void Accept(DString type, T1 x, T2 y);
void Accept(DText type, T1 x, T2 y);
void Accept(DBytes type, T1 x, T2 y);
void Accept(DText type, T1 x, T2 y);
void Accept(DVector2 type, T1 x, T2 y);
void Accept(DVector3 type, T1 x, T2 y);
void Accept(DVector4 type, T1 x, T2 y);
void Accept(DBean type, T1 x, T2 y);
@ -90,13 +98,5 @@ namespace Luban.Job.Cfg.DataVisitors
void Accept(DSet type, T1 x, T2 y);
void Accept(DMap type, T1 x, T2 y);
void Accept(DVector2 type, T1 x, T2 y);
void Accept(DVector3 type, T1 x, T2 y);
void Accept(DVector4 type, T1 x, T2 y);
void Accept(DDateTime type, T1 x, T2 y);
}
}

View File

@ -99,4 +99,53 @@ namespace Luban.Job.Cfg.DataVisitors
TR Accept(DDateTime type, T x);
}
public interface IDataFuncVisitor<T1, T2, TR>
{
TR Accept(DBool type, T1 x, T2 y);
TR Accept(DByte type, T1 x, T2 y);
TR Accept(DShort type, T1 x, T2 y);
TR Accept(DFshort type, T1 x, T2 y);
TR Accept(DInt type, T1 x, T2 y);
TR Accept(DFint type, T1 x, T2 y);
TR Accept(DLong type, T1 x, T2 y);
TR Accept(DFlong type, T1 x, T2 y);
TR Accept(DFloat type, T1 x, T2 y);
TR Accept(DDouble type, T1 x, T2 y);
TR Accept(DEnum type, T1 x, T2 y);
TR Accept(DString type, T1 x, T2 y);
TR Accept(DBytes type, T1 x, T2 y);
TR Accept(DText type, T1 x, T2 y);
TR Accept(DBean type, T1 x, T2 y);
TR Accept(DArray type, T1 x, T2 y);
TR Accept(DList type, T1 x, T2 y);
TR Accept(DSet type, T1 x, T2 y);
TR Accept(DMap type, T1 x, T2 y);
TR Accept(DVector2 type, T1 x, T2 y);
TR Accept(DVector3 type, T1 x, T2 y);
TR Accept(DVector4 type, T1 x, T2 y);
TR Accept(DDateTime type, T1 x, T2 y);
}
}

View File

@ -7,160 +7,158 @@ using System.Collections.Generic;
namespace Luban.Job.Cfg.DataVisitors
{
class ResourceExportor : IDataActionVisitor<DefField, List<ResourceInfo>>
class ResourceExportor : IDataActionVisitor<TType, List<ResourceInfo>>
{
public const string ResTagName = "res";
public static ResourceExportor Ins { get; } = new ResourceExportor();
public void Accept(DBool type, DefField x, List<ResourceInfo> y)
public void Accept(DBool type, TType x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DByte type, DefField x, List<ResourceInfo> y)
public void Accept(DByte type, TType x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DShort type, DefField x, List<ResourceInfo> y)
public void Accept(DShort type, TType x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DFshort type, DefField x, List<ResourceInfo> y)
public void Accept(DFshort type, TType x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DInt type, DefField x, List<ResourceInfo> y)
public void Accept(DInt type, TType x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DFint type, DefField x, List<ResourceInfo> y)
public void Accept(DFint type, TType x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DLong type, DefField x, List<ResourceInfo> y)
public void Accept(DLong type, TType x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DFlong type, DefField x, List<ResourceInfo> y)
public void Accept(DFlong type, TType x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DFloat type, DefField x, List<ResourceInfo> y)
public void Accept(DFloat type, TType x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DDouble type, DefField x, List<ResourceInfo> y)
public void Accept(DDouble type, TType x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DEnum type, DefField x, List<ResourceInfo> y)
public void Accept(DEnum type, TType x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DString type, DefField x, List<ResourceInfo> y)
public void Accept(DString type, TType x, List<ResourceInfo> y)
{
//if (!string.IsNullOrEmpty(type.Value))
//{
// y.Add(new ResourceInfo() { Resource = type.Value, Tag = x.ResourceTag });
//}
throw new NotSupportedException();
if (!string.IsNullOrEmpty(type.Value) && x.HasTag(ResTagName))
{
y.Add(new ResourceInfo() { Resource = type.Value, Tag = x.GetTag(ResTagName) });
}
}
public void Accept(DBytes type, DefField x, List<ResourceInfo> y)
public void Accept(DText type, TType x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DText type, DefField x, List<ResourceInfo> y)
public void Accept(DBytes type, TType x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DBean type, DefField _, List<ResourceInfo> y)
public void Accept(DDateTime type, TType x, List<ResourceInfo> y)
{
}
public void Accept(DVector2 type, TType x, List<ResourceInfo> y)
{
}
public void Accept(DVector3 type, TType x, List<ResourceInfo> y)
{
}
public void Accept(DVector4 type, TType x, List<ResourceInfo> y)
{
}
public void Accept(DBean type, TType x, List<ResourceInfo> y)
{
var def = type.ImplType;
if (def == null)
{
return;
}
//int index = 0;
//foreach (DType fieldData in type.Fields)
//{
// var fieldDef = (DefField)def.HierarchyFields[index++];
// if (fieldDef.IsResource)
// {
// fieldData.Apply(this, fieldDef, y);
// }
//}
throw new NotSupportedException();
int index = 0;
foreach (DType fieldData in type.Fields)
{
if (fieldData == null)
{
continue;
}
var fieldDef = ((DefField)def.HierarchyFields[index++]).CType;
fieldData.Apply(this, fieldDef, y);
}
}
private void Accept(DefField def, List<DType> datas, TType elementType, List<ResourceInfo> ress)
private void Accept(List<DType> datas, TType elementType, List<ResourceInfo> ress)
{
//if (def.IsResource || (elementType is TBean))
//{
// foreach (var e in datas)
// {
// e.Apply(this, def, ress);
// }
//}
throw new NotSupportedException();
foreach (var e in datas)
{
if (e != null)
{
e.Apply(this, elementType, ress);
}
}
}
public void Accept(DArray type, DefField x, List<ResourceInfo> y)
public void Accept(DArray type, TType x, List<ResourceInfo> y)
{
Accept(x, type.Datas, type.Type.ElementType, y);
Accept(type.Datas, type.Type.ElementType, y);
}
public void Accept(DList type, DefField x, List<ResourceInfo> y)
public void Accept(DList type, TType x, List<ResourceInfo> y)
{
Accept(x, type.Datas, type.Type.ElementType, y);
Accept(type.Datas, type.Type.ElementType, y);
}
public void Accept(DSet type, DefField x, List<ResourceInfo> y)
public void Accept(DSet type, TType x, List<ResourceInfo> y)
{
Accept(x, type.Datas, type.Type.ElementType, y);
Accept(type.Datas, type.Type.ElementType, y);
}
public void Accept(DMap type, DefField x, List<ResourceInfo> y)
public void Accept(DMap type, TType x, List<ResourceInfo> y)
{
//if (x.IsResource || (type.Type.ValueType is TBean))
//{
// foreach (var e in type.Datas.Values)
// {
// e.Apply(this, x, y);
// }
//}
throw new NotSupportedException();
}
public void Accept(DVector2 type, DefField x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DVector3 type, DefField x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DVector4 type, DefField x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DDateTime type, DefField x, List<ResourceInfo> y)
{
throw new NotImplementedException();
TMap mtype = (TMap)x;
foreach (var (k, v) in type.Datas)
{
k.Apply(this, mtype.KeyType, y);
v.Apply(this, mtype.ValueType, y);
}
}
}
}

View File

@ -12,8 +12,7 @@ namespace Luban.Job.Cfg.DataVisitors
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))}\"}}";
return $"#{{{DText.KEY_NAME}=>\"{type.Key}\",{DText.TEXT_NAME}=>\"{DataUtil.EscapeString(type.TextOfCurrentAssembly)}\"}}";
}
public override string Accept(DBean type)
@ -21,7 +20,7 @@ namespace Luban.Job.Cfg.DataVisitors
var x = new StringBuilder();
if (type.Type.IsAbstractType)
{
x.Append($"#{{name__ => \"{type.ImplType.Name}\"");
x.Append($"#{{name__ => \"{DataUtil.GetImplTypeName(type)}\"");
if (type.Fields.Count > 0)
{
x.Append(',');

View File

@ -12,8 +12,7 @@ namespace Luban.Job.Cfg.DataVisitors
public override string Accept(DText type)
{
var ass = DefAssembly.LocalAssebmly;
return $"{{\"{DText.KEY_NAME}\":\"{type.Key}\",\"{DText.TEXT_NAME}\":\"{DataUtil.EscapeString(type.GetText(ass.ExportTextTable, ass.NotConvertTextSet))}\"}}";
return $"{{\"{DText.KEY_NAME}\":\"{type.Key}\",\"{DText.TEXT_NAME}\":\"{DataUtil.EscapeString(type.TextOfCurrentAssembly)}\"}}";
}
public override string Accept(DBean type)

View File

@ -7,7 +7,7 @@ namespace Luban.Job.Cfg.DataVisitors
{
abstract class ToLiteralVisitorBase : IDataFuncVisitor<string>
{
public string Accept(DBool type)
public virtual string Accept(DBool type)
{
return type.Value ? "true" : "false";
}
@ -92,8 +92,7 @@ namespace Luban.Job.Cfg.DataVisitors
public virtual string Accept(DDateTime type)
{
var ass = DefAssembly.LocalAssebmly as DefAssembly;
return type.GetUnixTime(ass.TimeZone).ToString();
return type.UnixTimeOfCurrentAssembly.ToString();
}
}
}

View File

@ -10,10 +10,14 @@ namespace Luban.Job.Cfg.DataVisitors
{
public static ToLuaLiteralVisitor Ins { get; } = new();
public override string Accept(DString type)
{
return DataUtil.EscapeLuaStringWithQuote(type.Value);
}
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))}\"}}";
return $"{{{DText.KEY_NAME}='{type.Key}',{DText.TEXT_NAME}={DataUtil.EscapeLuaStringWithQuote(type.TextOfCurrentAssembly)}}}";
}
public override string Accept(DBean type)
@ -21,7 +25,7 @@ namespace Luban.Job.Cfg.DataVisitors
var x = new StringBuilder();
if (type.Type.IsAbstractType)
{
x.Append($"{{ _name='{type.ImplType.Name}',");
x.Append($"{{ {DefBean.LUA_TYPE_NAME_KEY}='{DataUtil.GetImplTypeName(type)}',");
}
else
{

View File

@ -10,10 +10,14 @@ namespace Luban.Job.Cfg.DataVisitors
{
public static ToPythonLiteralVisitor Ins { get; } = new();
public override string Accept(DBool type)
{
return type.Value ? "True" : "False";
}
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))}\"}}";
return $"{{\"{DText.KEY_NAME}\":\"{type.Key}\",\"{DText.TEXT_NAME}\":\"{DataUtil.EscapeString(type.TextOfCurrentAssembly)}\"}}";
}
public override string Accept(DBean type)
@ -95,7 +99,7 @@ namespace Luban.Job.Cfg.DataVisitors
x.Append(',');
}
++index;
x.Append('"').Append(e.Key.ToString()).Append('"');
x.Append(e.Key.Apply(this));
x.Append(':');
x.Append(e.Value.Apply(this));
}

View File

@ -11,12 +11,7 @@ namespace Luban.Job.Cfg.DataVisitors
public override string Accept(DText type)
{
#if !LUBAN_LITE
var ass = DefAssembly.LocalAssebmly as DefAssembly;
return $"\"{type.Key}#{type.GetText(ass.ExportTextTable, ass.NotConvertTextSet)}\"";
#else
return $"\"{type.Key}#{type.RawValue}\"";
#endif
return $"\"{type.Key}#{type.TextOfCurrentAssembly}\"";
}
public override string Accept(DBean type)

Some files were not shown because too many files have changed in this diff Show More