Compare commits
227 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
720e2eaf49 | |
|
|
7c0579b4bb | |
|
|
b791b4276d | |
|
|
de075ca7d9 | |
|
|
803145e267 | |
|
|
ea1dc4a462 | |
|
|
62b60ac581 | |
|
|
9bc74b3c73 | |
|
|
4ef2d70398 | |
|
|
8e68ab0fcd | |
|
|
bcf81ffe26 | |
|
|
c14437aa59 | |
|
|
16e4fe2452 | |
|
|
7ad942b7cd | |
|
|
e8ac256e92 | |
|
|
46cc7cd26f | |
|
|
e67082a098 | |
|
|
ae87bf3f52 | |
|
|
ea30f8c202 | |
|
|
874f966a40 | |
|
|
b37f2692e3 | |
|
|
ba2c5c56e7 | |
|
|
d0587324f5 | |
|
|
2962b6a1bf | |
|
|
44b0fc9b36 | |
|
|
33b7f25fd4 | |
|
|
9b97f5d0bd | |
|
|
88ab94e836 | |
|
|
410fdd3618 | |
|
|
bcc7bc8b37 | |
|
|
165ad69a5d | |
|
|
b1c2507d99 | |
|
|
af492cea8d | |
|
|
30a4ce12db | |
|
|
14faa26565 | |
|
|
472c912f0f | |
|
|
e897d6b5c0 | |
|
|
70b5a9b061 | |
|
|
15a5a627d5 | |
|
|
c707935008 | |
|
|
81295cbfe1 | |
|
|
d9013167b8 | |
|
|
ad4acf337f | |
|
|
f25f91b94d | |
|
|
741d820587 | |
|
|
c1b4070520 | |
|
|
4e2891e07b | |
|
|
b1f9105773 | |
|
|
69a0acf63e | |
|
|
ff6e09ec9d | |
|
|
c60f91fdd4 | |
|
|
154d55f372 | |
|
|
2dd826bafb | |
|
|
412a400471 | |
|
|
c738b4dcad | |
|
|
a39339bd51 | |
|
|
699b5a167a | |
|
|
665b9a4cea | |
|
|
abcacebcaa | |
|
|
c0706a84f1 | |
|
|
bfc13bc35f | |
|
|
02f3ebd979 | |
|
|
79940dcf09 | |
|
|
6620cca6e1 | |
|
|
d6ad1e64c4 | |
|
|
c0ddce497c | |
|
|
4255ae25bf | |
|
|
ba1a552d3e | |
|
|
60faab741e | |
|
|
f756ab2aa2 | |
|
|
9b086767a1 | |
|
|
c39b6a623d | |
|
|
827eca027c | |
|
|
e3dc909514 | |
|
|
6d991ef000 | |
|
|
3f2b2c71eb | |
|
|
861f40c29b | |
|
|
d9a2e0f83b | |
|
|
f7814a0612 | |
|
|
142e3ddf93 | |
|
|
7b7ab7b8ba | |
|
|
df932046e4 | |
|
|
7bc489e15e | |
|
|
82fbdb513f | |
|
|
c146cf6031 | |
|
|
ee6bba3414 | |
|
|
9886bd1222 | |
|
|
71185c4f3d | |
|
|
ced29f9fca | |
|
|
dee9bcdfe0 | |
|
|
4cc52c25a3 | |
|
|
69b54fcd91 | |
|
|
fc0772d6f6 | |
|
|
68c48d0c02 | |
|
|
856f30df5b | |
|
|
0e3782dd48 | |
|
|
aab29ed828 | |
|
|
c0d91dfa81 | |
|
|
e977a363ef | |
|
|
480b839d31 | |
|
|
2335a82c11 | |
|
|
7901b7012b | |
|
|
c79e6169a6 | |
|
|
13ee28835a | |
|
|
b2014ab7bb | |
|
|
d5184e136e | |
|
|
3631f131ad | |
|
|
034f0dd21b | |
|
|
c52407bdee | |
|
|
ba269df862 | |
|
|
657bd19c77 | |
|
|
e76b20b24f | |
|
|
caa476272b | |
|
|
953469475c | |
|
|
e8805873ea | |
|
|
e6203df5b3 | |
|
|
6b1669e050 | |
|
|
45f4a13dd2 | |
|
|
0b63b1fcb3 | |
|
|
66e07830d0 | |
|
|
0122d97071 | |
|
|
7d05f1fd4d | |
|
|
a4df2de837 | |
|
|
f3949f9c72 | |
|
|
1a1fc3ca2f | |
|
|
54e1955112 | |
|
|
b3457da8d2 | |
|
|
9f687adcf1 | |
|
|
7f9c4e93d6 | |
|
|
29cf3b56f6 | |
|
|
f617d5f10f | |
|
|
1acaac5b87 | |
|
|
c20505aea3 | |
|
|
f9abe09a8d | |
|
|
582edb6cc6 | |
|
|
a41fd92b43 | |
|
|
1e06690def | |
|
|
c612423590 | |
|
|
d40c016afa | |
|
|
36df70bffb | |
|
|
8cd9e5f247 | |
|
|
a560c23c67 | |
|
|
c009d54d26 | |
|
|
fc1cb23974 | |
|
|
4bd826a360 | |
|
|
00a56c0a37 | |
|
|
8164d641e4 | |
|
|
51dbe96917 | |
|
|
8e4ef0952d | |
|
|
b27366932d | |
|
|
73da39e7cd | |
|
|
7a80812c17 | |
|
|
2021e38485 | |
|
|
c2fc86215c | |
|
|
6bc3552a1f | |
|
|
9e6463f18d | |
|
|
4f8fd20780 | |
|
|
8cd9ffee3c | |
|
|
fb000e400c | |
|
|
e650ff6fe6 | |
|
|
aa0d0a9a36 | |
|
|
c1ffaa2d98 | |
|
|
5861001683 | |
|
|
a78c9abd1a | |
|
|
1377eb828d | |
|
|
19656437cb | |
|
|
83f81ed463 | |
|
|
93595c7748 | |
|
|
79d52c8820 | |
|
|
a6a1d58159 | |
|
|
f740685830 | |
|
|
8b0f607f4a | |
|
|
6bcc8e74ce | |
|
|
91bb1b317f | |
|
|
edc09b6742 | |
|
|
7fc75871d4 | |
|
|
ff93a074c4 | |
|
|
85937685b6 | |
|
|
ba4bb014a9 | |
|
|
967c45dda1 | |
|
|
46ccf6868b | |
|
|
7df2f6ea86 | |
|
|
57867bc14b | |
|
|
ca8e5035ba | |
|
|
d686f82ba8 | |
|
|
d571638970 | |
|
|
56c222976e | |
|
|
6e83d8c1eb | |
|
|
66ba09e8a0 | |
|
|
b8e794d528 | |
|
|
813e129352 | |
|
|
a9de6b924b | |
|
|
1de0299b17 | |
|
|
e9244a30da | |
|
|
bb534ef12e | |
|
|
28626d0cfd | |
|
|
b0d64bb967 | |
|
|
7ff3467d35 | |
|
|
edd82f249c | |
|
|
9083915b62 | |
|
|
675f9954cf | |
|
|
fa971c0e06 | |
|
|
41fd0a908b | |
|
|
3a021f74a8 | |
|
|
8cd45782f4 | |
|
|
5e56d35e4a | |
|
|
f024ec6974 | |
|
|
35082d96d5 | |
|
|
fcc12ab0d2 | |
|
|
9eacac3171 | |
|
|
4f0addec89 | |
|
|
21a745a4f7 | |
|
|
8c02821fd8 | |
|
|
e9f608a238 | |
|
|
8e8fe68fd0 | |
|
|
d9e7bc3914 | |
|
|
9fd76d5334 | |
|
|
2034e8050f | |
|
|
f6b8b32123 | |
|
|
9cc489ecfd | |
|
|
390a72747d | |
|
|
a11380d142 | |
|
|
fd0817d630 | |
|
|
5a21c9b1b4 | |
|
|
afbfaa1c55 | |
|
|
28c6ee75a0 | |
|
|
66db58d3fb |
|
|
@ -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
|
||||
|
|
|
|||
1250
README.en-us.md
1250
README.en-us.md
File diff suppressed because it is too large
Load Diff
|
|
@ -1 +1 @@
|
|||
theme: jekyll-theme-cayman
|
||||
theme: jekyll-theme-slate
|
||||
126
docs/catalog.md
126
docs/catalog.md
|
|
@ -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 工作模型
|
||||

|
||||
* 定义 + 数据的 抽象模型
|
||||
* 定义
|
||||
* 根定义文件
|
||||
* 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
|
||||
|
|
@ -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” 不是合法的整数值。
|
||||

|
||||
- 填写这些占据多个单元格的数据有两种办法:
|
||||
1. 合并标题头
|
||||
让 level_range 标题头占据两列,这样就能在两个单元格里分别填写最小最大等级了。
|
||||

|
||||
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=”|“ 即可。
|
||||

|
||||
|
||||
## 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>
|
||||
```
|
||||
|
||||
配置:
|
||||

|
||||
|
||||
- 对于多个值构成的字段,填写方式为 在标题头(level_attrs)对应的列范围内,按顺序填值。不需要跟策划的标题头名有对应关系。空白单元格会被忽略。也就是如下填法也是可以的:
|
||||

|
||||
- 这种填法的缺点是占据在太多的列。如果想如下填,该怎么办呢?
|
||||

|
||||
- 有两种办法。
|
||||
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>
|
||||
```
|
||||
如果想所有数据都在一个单元格内填写,又该怎么办呢?
|
||||

|
||||
想用 | 来分割不同 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>
|
||||
```
|
||||
配置:
|
||||

|
||||
- 注意到,多态 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>
|
||||
```
|
||||
- 配置
|
||||

|
||||
- 使用类型名和别名来标识多态都是支持的,可以混合使用。
|
||||
|
||||
## 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” 字段,表示这个字段要多行配置。
|
||||
- 
|
||||
|
||||
## 多级标题头
|
||||
|
||||
- 经常会有字段占了多列,比如 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>
|
||||
```
|
||||
- 配置
|
||||

|
||||
- 有几处改动
|
||||
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"/>
|
||||
```
|
||||
- 配置
|
||||

|
||||
|
||||
## 横表与纵表
|
||||
|
||||
- 之前介绍的表都是 面向行,沿着行方向填写数据。有时候我们希望 以列为方向填写。
|
||||
- 比如 上面的单例表。 如果改成一行一个字段,看起来会更清楚。 我们引入纵表支持。
|
||||
- 定义不变,但 excel 的填法有区别,数据如下:
|
||||
- 
|
||||
|
||||
|
||||
## 默认值
|
||||
|
||||
该特性只对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"/>
|
||||
|
||||
```
|
||||
|
||||

|
||||
|
||||
## 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>
|
||||
```
|
||||
|
||||
配置:
|
||||
|
||||

|
||||
|
|
@ -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}]
|
||||
}
|
||||
```
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
[//]: # (Author: bug)
|
||||
[//]: # (Date: 2020-11-01 16:26:41)
|
||||
|
||||
# Lua 数据
|
||||
|
||||
##
|
||||
|
||||
* 与 json 相似定义。
|
||||
* 唯一区别在于, lua 的table的key支持任意格式,所以 lua 的map 可以直接 {[key1] = value1, [key2] = value2, ,,,}
|
||||
* 
|
||||
* 注意 数据前有一个 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} },
|
||||
}
|
||||
```
|
||||
|
|
@ -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>
|
||||
```
|
||||
|
|
@ -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
|
||||
|
||||
```
|
||||
|
|
@ -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
|
||||
```
|
||||
26
docs/faq.md
26
docs/faq.md
|
|
@ -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 中用一些技巧实现更舒服。
|
||||
199
docs/feature.md
199
docs/feature.md
|
|
@ -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使用。
|
||||
定义如下:
|
||||
|
||||

|
||||
|
||||
#### 字段级别分组
|
||||
|
||||
定义方式为给var指定group属性,未指定则默认导出给所有分组。可以为多个,以逗号","分隔。相比于大多数导表工具只支持**表顶级字段**的分组导出,luban支持任意bean字段粒度级别的分组导出。
|
||||
|
||||
例如, TbDemoGroup表中 id,x1,x4 字段前后端都需要; x3 只有后端需要;x2 字段只有前端需要。x5是bean类型,它导出给前后端,但它的子字段也可以被分组过滤, x5.y1, x2.y4前后端都会导出,x5.x3只导出给后端,x5.x2只导出给前端。
|
||||
定义如下:
|
||||
|
||||

|
||||
|
||||
|
||||
### 生成极快
|
||||
* 大项目几十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的 其他任何引擎
|
||||
|
||||
### 强大的扩展能力
|
||||
* 支持插件形式,扩展其他生成极其方便
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
157
docs/l10n.md
157
docs/l10n.md
|
|
@ -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" 指定目标时区为 亚洲上海(也即北京时间)
|
||||
|
|
@ -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_json,code_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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
|
||||
# TODO 待补充
|
||||
|
|
@ -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
|
||||
```
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
## road map 1
|
||||
|
||||
* 补充单元测试
|
||||
* 新增静态本地化支持
|
||||
* 新增动态本地化支持
|
||||
* 新增生成用于unreal编辑器的 加载及保存json配置的cpp代码
|
||||
* 新增生成用于unity 编辑器的 加载及保存json配置的c#代码
|
||||
|
||||
## road map 2
|
||||
|
||||
* 新增 unity 内嵌编辑器
|
||||
* 新增 unreal 内嵌编辑器
|
||||
|
||||
|
|
@ -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
|
||||
```
|
||||
106
docs/start_up.md
106
docs/start_up.md
|
|
@ -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” 文件。
|
||||
|
||||

|
||||
|
||||
文件内容如下
|
||||
|
||||
|##|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 == 。
|
||||
|
||||
类似这样
|
||||
|
||||

|
||||
|
||||
|
||||
如果一切顺利。生成的代码文件会在 –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. 至此完成 配置加载与使用!
|
||||
137
docs/traits.md
137
docs/traits.md
|
|
@ -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的引擎和平台
|
||||
|
|
@ -1,2 +1,6 @@
|
|||
**/bin/
|
||||
**/obj/
|
||||
/lubanAssistant/
|
||||
/packages/
|
||||
/TestResults/
|
||||
LubanTools.sln
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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 ./
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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"]
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
docker build -t luban-clientserver:latest -f Dockerfile ../..
|
||||
pause
|
||||
|
|
@ -0,0 +1 @@
|
|||
docker build -t luban-clientserver:latest -f Dockerfile ../..
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
docker tag luban-clientserver:latest focuscreativegames/luban-clientserver:latest
|
||||
docker push focuscreativegames/luban-clientserver:latest
|
||||
pause
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
docker tag luban-server:latest focuscreativegames/luban-server:latest
|
||||
docker push focuscreativegames/luban-server:latest
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)}"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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}");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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('}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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不支持单例读取模式");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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++)
|
||||
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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, "", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(',');
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue