[init] add initial project files

main
walon 2020-10-21 21:25:28 +08:00
parent 6c7dbb23f1
commit 3a3ab46fac
248 changed files with 25152 additions and 0 deletions

63
.gitattributes vendored 100644
View File

@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

267
.gitignore vendored 100644
View File

@ -0,0 +1,267 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
ReaderLib/**/Config
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
project.fragment.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
# *.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
#*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# gen cache meta file
.cache.meta

62
README.en-us.md 100644
View File

@ -0,0 +1,62 @@
# Birght Gen
## What is Bright Gen
* Aim
* Less time for trival & repeated work, more time with creativity & leisure .
* Philosophy
* Simpler, Faster
* [Home Page](https://focus-creative-games.github.io/bright_gen/index.html)
* Read this in other languages: [English](README.en-us.md), [简体中文](README.md)
## Features
* [x] multi data source (json, excel, folder)
* [ ] data type system supported
* [ ] polymorphism
* [ ] rich embeded type
* [ ] user defined type
* [ ] client/server structure & export faster
* [ ] shared cache
* [ ] customizable validataor
* [ ] customizable export format
* [ ] easy to extend with new feature
* [ ] enhanced support with excel
* [ ] dual key
* [ ] horizontal list
* [ ] shared cache for export time optimization
* [ ] sophisticated/polished source available
* [ ] localization & region support
## How to set self hosted server up
* Windows
* run xxx.bat
* Docker
* run xxx.bat/xxx.sh
* Other
* any .Net core environment
## How to set development up
* VS2019 commuity
## How can I contribute?
We welcome contributions! Many people all over the world have helped make this project better.
* [Contributing](CONTRIBUTING.md) explains what kinds of changes we welcome
- [Workflow Instructions](docs/workflow/README.md) explains how to build and test
## Useful Links
* [.NET Core source index](https://source.dot.net) / [.NET Framework source index](https://referencesource.microsoft.com)
* other implementation
* [tabtoy](https://github.com/davyxu/tabtoy)
## License
Birght Gen is licensed under the [MIT](LICENSE.TXT) license.

88
README.md 100644
View File

@ -0,0 +1,88 @@
[//]: # (Author: bug)
[//]: # (Date: 2020-10-20 20:24:07)
# Gen
## 什么是 Gen
Gen 是一个强大的生成与缓存工具,用于但不限于 游戏配置、消息、资源格式转换 之类的生成。
相比传统简单的以excel为中心的表格导出工具它提供了一个**完整的游戏配置数据解决方案**,有以下功能:
>
> * 数据定义
> * 数据编辑
> * 数据导出
> * 前后端代码生成
> * 本地化
> * 编辑器数据load&save代码生成
Gen能够良好满足小型、中型、大型及超大型游戏项目的配置需求。
Gen 工具不仅适用于游戏行业,也非常适合传统的互联网项目。
## 文档
* [主页](https://focus-creative-games.github.io/bright_gen/index.html)
* 各语言的简介: [English](README.en-us.md), [简体中文](README.md)
## 使用示例
* Lua 使用示例
``` Lua
local data = require("TbDataFromJson")
local cfg = data[32]
print(cfg.name)
```
* [更多语言的例子](docs/samples.md)
## 特性
* [完备的数据类型支持](docs/feature.md#支持的数据类型)
* [多类型数据源支持](docs/feature.md#多数据源支持)
* [多种数据表模式](docs/feature.md#多种数据表模式)
* [按组导出数据](docs/feature.md#如何自定义导出分组)
* [生成速度快](docs/feature.md#生成极快)
* [增强 Excel 的表达](docs/feature.md#增强的-excel-格式)
* [代码提示支持](docs/feature.md#代码编辑器支持)
* [根据开发效率需求定制的数据输出格式](docs/feature.md#支持多种导出数据格式)
* [本地化支持](docs/feature.md#本地化支持)
* [代码提示支持](docs/feature.md#代码编辑器支持)
* [强大的数据校验能力](docs/feature.md#强大的数据校验能力)
* [资源导出支持](docs/feature.md#资源导出支持)
* [自动代码生成](docs/feature.md#优秀的代码生成)
* [数据分组](docs/feature.md#良好的数据组织)
* [多语言支持](docs/feature.md#支持的语言-覆盖主流的语言)
* [多服务器引擎支持](docs/feature.md#支持的服务器引擎-满足语言版本的情况下)
* [多客户端引擎支持](docs/feature.md#支持的客户端引擎-满足语言版本的情况下)
* [扩展能力](docs/feature.md#强大的扩展能力)
* [ ] 提供定制开发服务 ^_^
## RoadMap
- [ ] 新增 unity 内置编辑器
- [ ] 新增 unreal 内置编辑器
- [ ] 补充单元测试
- [x] 支持 python
## 布署
TODO
## 开发环境架设
* 安装 VS2019 社区版
* 安装 .dotnet core sdk 3.1
## 如何贡献?
* [Contributing](CONTRIBUTING.md) explains what kinds of changes we welcome
- [Workflow Instructions](docs/workflow/README.md) explains how to build and test
## Useful Links
* [.NET Core source index](https://source.dot.net)
* 社区的其它实现
* [tabtoy](https://github.com/davyxu/tabtoy)
## 支持和联系
QQ 群: 692890842
邮箱: taojingjian#gmail.com
## License
Birght Gen is licensed under the [MIT](LICENSE.TXT) license.

Binary file not shown.

View File

@ -0,0 +1,30 @@
{
"x1":true,
"x2":3,
"x3":128,
"x4":1211,
"x5":11223344,
"x6":1.2,
"x7":1.23432,
"x8_0":12312,
"x8":112233,
"x9":223344,
"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],
"k3":[1,3],
"k4":[1,5],
"k5":[1,6],
"k6":[1,7],
"k7":[2,3],
"k8":[[2,2],[4,10]],
"k9":[{"y1":1, "y2":true},{"y1":2, "y2":false}],
"k15":[{"__type__": "DemoD2", "x1":1, "x2":2}]
}

View File

@ -0,0 +1,31 @@
return
{
x1 = false,
x2 = 2,
x3 = 128,
x4 = 1122,
x5 = 112233445566,
x6 = 1.3,
x7 = 1122,
x8 = 12,
x8_0 = 13,
x9 = 123,
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},
k3 = {3,4},
k4 = {1,2},
k5 = {1,3},
k6 = {1,2},
k7 = {1,8},
k8 = {[2]=10,[3]=12},
k9 = {{y1=1,y2=true}, {y1=10,y2=false}},
k15 = {{ __type__="DemoD2", x1 = 1, x2=3}},
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,12 @@
##,align:true,row:true,,,,,,,,,,,,,,,,,,,,,,
__type__,x1,x2,x3,x4,x5,x6,x7,x8,x8_0,x9,x10,x11,x12,x13,,,k1,k2,k3,k4,k5,k6,k7,k8
,½ûÖ¹,x2:byte,x3:short,x4:int,x5:long, x6:float,x7:double,,,,,,,,,,array:int,array:int,array:int,array:int,array:int,array:int,array:int,map:int:int
DemoD2,TRUE,5,5,10000,13234234234,1.28,1.23457891,1234,1234,111111111,huang,,1988,A,,,"1,2,3","1,2,4","1,2,5","1,2,6","1,2,7","1,2,8","1,2,9","1,2,3,4"
,FALSE,0,6,198704,34523452345,2.5,19870421.2,453234,-345,112233445566 ,qiang,,1987,B,,,"2,4,6","2,4,7","2,4,8","2,4,9","2,4,10","2,4,11","2,4,12","1,10,2,20"
,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,
Can't render this file because it contains an unexpected character in line 4 and column 152.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,24 @@
{
"x1" : true,
"x2" : 5,
"x3" : 1234,
"x4" : 120000,
"x5" : 12345566778899,
"x6" : 1.28,
"x7" : 123456789.1234567,
"x8" : 1234,
"x8_0" : 1122,
"x9" : 112233445566,
"x10": "huang",
"x11": "hiasf",
"x12" : { "x1":1987 },
"x13" : "B",
"k1" : [1,2,3],
"k2" : [11,22,33],
"k3" : [1,2,3],
"k4": [11,22],
"k5" : [2,3],
"k6" : [4,5],
"k7" : [10,20],
"k8" : { "1":100, "2":200, "3":300}
}

View File

@ -0,0 +1,78 @@
<data>
<x1>true</x1>
<x2>4</x2>
<x3>128</x3>
<x4>1122</x4>
<x5>112233445566</x5>
<x6>1.3</x6>
<x7>1112232.43123</x7>
<x8>112233</x8>
<x8_0>123</x8_0>
<x9>112334</x9>
<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>
<k3>
<item>1</item>
<item>2</item>
</k3>
<k4>
<item>1</item>
<item>2</item>
</k4>
<k5>
<item>1</item>
<item>2</item>
</k5>
<k6>
<item>1</item>
<item>2</item>
</k6>
<k7>
<item>1</item>
<item>3</item>
</k7>
<k8>
<item> <key>2</key><value>10</value></item>
<item> <key>3</key><value>30</value></item>
</k8>
<k9>
<item>
<y1>1</y1>
<y2>true</y2>
</item>
<item>
<y1>2</y1>
<y2>false</y2>
</item>
</k9>
<k15>
<item __type__="DemoD2">
<x1>1</x1>
<x2>2</x2>
</item>
</k15>
</data>

View File

@ -0,0 +1,23 @@
<root>
<topmodule name="cfg"/>
<group name="client,c" default="1"/>
<group name="server,s" default="1"/>
<group name="editor,e" default="1"/>
<group name="ue,u"/>
<import name="."/>
<service name="server" manager="Tables" group="s,server"/>
<service name="client" manager="Tables" group="c,client"/>
<service name="all" manager="Tables" group="c,s"/>
<service name="ue4_editor" manager="Tables" group="e"/>
<service name="unity_editor" manager="Tables" group="e"/>
<service name="ue4" manager="Tables" group="u,ue"/>
</root>

View File

@ -0,0 +1,212 @@
<module name="test">
<const name="DemoConst">
<var name="x1" type="int" value="0"/>
<var name="x2" type="long" value="3242"/>
<var name="x3" type="float" value="444.3"/>
<var name="x4" type="double" value="55.3"/>
<!--var name="x5" type="string" value="abcdefg"/-->
</const>
<enum name="DemoEnum">
<var name="A" value="1"/>
<var name="B"/>
<var name="C" value="4"/>
<var name="D"/>
</enum>
<bean name="DemoType1">
<var name="x1" type="int"/>
</bean>
<bean name="DemoDynamic"> 多态数据结构
<var name="x1" type="int"/>
<bean name="DemoD2" alias="测试别名">
<var name="x2" type="int"/>
</bean>
<bean name="DemoD3">
<var name="x3" type="int"/>
<bean name="DemoE1">
<var name="x4" type="int"/>
</bean>
</bean>
<bean name="DemoD5">
<var name="time" type="DateTimeRange"/>
</bean>
</bean>
<bean name="DemoE2">
<var name="y1" type="int"/>
<var name="y2" type="bool"/>
</bean>
<bean name="DemoType2" >
<var name="x1" type="bool"/>
<var name="x2" type="byte"/>
<var name="x3" type="short" ref="test.TbFullTypes"/>
<var name="x4" type="int?" convert="DemoEnum"/>
<var name="x5" type="long" convert="DemoEnum"/>
<var name="x6" type="float"/>
<var name="x7" type="double"/>
<var name="x8_0" type="fshort"/>
<var name="x8" type="fint"/>
<var name="x9" type="flong"/>
<var name="x10" type="string" path="normal;*.txt"/>
<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="k3" type="linkedlist,int"/>
<var name="k4" type="arraylist,int"/>
<var name="k5" type="set,int"/>
<var name="k6" type="treeset,int"/>
<var name="k7" type="hashset,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="TbFullTypes" index="x3" value="DemoType2" input="test/full_type.xlsx"/> 最常见的普通 key-value表
<bean name="DateTimeRange" sep=";">
<var name="start_time" type="datetime"/>
<var name="end_time" type="datetime"/>
</bean>
<bean name="DemoSingletonType">
<var name="id" type="int"/>
<var name="name" type="text"/>
<var name="date" type="DemoDynamic"/>
</bean>
<table name="TbSingleton" mode="one" value="DemoSingletonType" input="test/table_one.xlsx"/> 单例表,只有一个记录
<table name="TbDataFromJson" value="DemoType2" input="test/json_datas"/> 普通表不过数据从tbrole_datas目录递归读入每个文件是一个记录
<table name="TbDataFromXml" value="DemoType2" input="test/xml_datas"/> 普通表不过数据从tbrole_datas目录递归读入每个文件是一个记录
<table name="TbDataFromLua" value="DemoType2" input="test/lua_datas"/> 普通表不过数据从tbrole_datas目录递归读入每个文件是一个记录
<table name="TbTwoKey" value="DemoType2" index="x3,x4" input="test/json_datas" mode="bmap"/>
<bean name="MultiRowType1">
<var name="id" type="int"/>
<var name="x" type="int"/>
</bean>
<bean name="MultiRowType2">
<var name="id" type="int"/>
<var name="x" type="int"/>
<var name="y" type="float"/>
</bean>
<bean name="MultiRowRecord">
<var name="id" type="int"/>
<var name="name" type="string"/>
<var name="one_rows" type="list,MultiRowType1"/>
<var name="multi_rows1" type="list,MultiRowType1" multi_rows="1"/>
<var name="multi_rows2" type="array,MultiRowType1" multi_rows="1"/>
<var name="multi_rows3" type="set,MultiRowType2" multi_rows="1"/>
<var name="multi_rows4" type="map,int,MultiRowType2" multi_rows="1"/>
</bean>
<table name="TbMultiRowRecord" value="MultiRowRecord" input="test/multi_rows_record.xlsx"/>
<enum name="ETestUeType">
<var name="WHITE" alias="白"/>
<var name="BLACK"/>
</enum>
<enum name="ETestEmptyEnum">
</enum>
<enum name="ETestEmptyEnum2">
<var name="SMALL_THAN_256" value="255"/>
<var name="X_256" value="256"/>
<var name="X_257" value="257"/>
</enum>
<bean name="TestUeType">
<var name="x1" type="bool"/>
<var name="x2" type="byte"/>
<!--var name="x3" type="short"/-->
<var name="x4" type="int"/>
<var name="x5" type="long"/>
<var name="x6" type="float"/>
<!--var name="x7" type="double"/>
<var name="x8_0" type="fshort"/>
<var name="x8" type="fint"/>
<var name="x9" type="flong"/-->
<var name="x10" type="string"/>
<var name="x12" type="DemoType1"/>
<var name="x13" type="ETestUeType"/>
<!--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="x15" type="DemoType1"/>
<var name="y1" type="int?"/> nullable ,多态结构肯定是nullable的
-->
<var name="k1" type="array,int"/> 使用;来分隔
<var name="k2" type="list,int" group="c,s"/>
<var name="k3" type="linkedlist,int"/>
<var name="k4" type="arraylist,int"/>
<var name="k5" type="set,int"/>
<var name="k6" type="treeset,int"/>
<var name="k7" type="hashset,int"/>
<var name="k8" type="map,int,int"/>
<var name="k9" type="list,DemoE2"/>
</bean>
<bean name="H1">
<var name="y2" type="H2"/>
<var name="y3" type="int"/>
</bean>
<bean name="H2">
<var name="z2" type="int"/>
<var name="z3" type="int"/>
</bean>
<bean name="MultiRowTitle">
<var name="id" type="int"/>
<var name="name" type="string"/>
<var name="x1" type="H1"/>
<var name="x2" type="list,H2" multi_rows="1"/>
<var name="x3" type="array,H2" multi_rows="1"/>
</bean>
<table name="TbMultiRowTitle" value="MultiRowTitle" input="test/multi_level_title.xlsx"/>
<!--table name="TbDynamic" value="DemoDynamic" input="多态数据源"/-->
<module name="login">
支持在一个定义文件中 定义多个模块。 一般来说一个定义文件中一个模块比较好,但有些情况下为了方便可以定义多个。
<bean name="RoleInfo">
<var name="role_id" type="long"/>
</bean>
</module>
</module>

View File

@ -0,0 +1,3 @@
..\src\Luban.Client\bin\Debug\netcoreapp3.1\gen.client -h 127.0.0.1 -j cfg -- -d Defines/root.xml --input_data_dir Datas --output_code_dir output_code --output_data_dir output_data -s server --gen_types code_cs_bin,data_bin --export_test_data
pause

View File

@ -0,0 +1,2 @@
**/bin/
**/obj/

View File

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

View File

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.8.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Luban.Common\Luban.Common.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,81 @@
{
"profiles": {
"Luban.Client": {
"commandName": "Project",
"commandLineArgs": "-h 127.0.0.1 -p 8899 -j cfg -a \"{ 'define_file':'../../CfgDefines/root.xml', 'input_data_dir':'../../CfgDatas', 'validate_root_dir':'E:/NikkiP4_D/X6Game/Content', 'output_data_dir':'output_data', 'service':'server', 'export_test_data':true,'gen_types':'code_cs_bin,data_bin', 'output_code_dir' : 'Gen' }\"",
"workingDirectory": "E:\\workspace\\bright_gen\\DemoProjects\\Csharp_Server_DotNetCore"
},
"Luban.Client-db": {
"commandName": "Project",
"commandLineArgs": "-h 127.0.0.1 -p 8899 -j db -d ../../../../Test/root.db.xml -c F:\\workspace\\perfect_core\\BaseDemo\\Gen\\Db2 -t server -l cs"
},
"gen_gate_proto": {
"commandName": "Project",
"commandLineArgs": "-h 127.0.0.1 -p 8899 -j proto -d F:\\workspace\\all_server\\ProtoDefines\\gate.xml -c F:\\workspace\\all_server\\Gate\\Source\\Gen\\Proto -t server -l cs"
},
"gen_client_proto": {
"commandName": "Project",
"commandLineArgs": "-h 127.0.0.1 -p 8899 -j proto -d F:\\workspace\\all_server\\ProtoDefines\\client.xml -c F:\\workspace\\all_server\\DemoClient\\Source\\Gen\\Proto -t server -l cs"
},
"gen_base_db": {
"commandName": "Project",
"commandLineArgs": "-h 127.0.0.1 -p 8899 -j db -d F:\\workspace\\all_server\\DbDefines\\base.xml -c F:\\workspace\\all_server\\Base\\Source\\Gen\\Db -t server -l cs"
},
"gen_base_proto": {
"commandName": "Project",
"commandLineArgs": "-h 127.0.0.1 -p 8899 -j proto -d F:\\workspace\\all_server\\ProtoDefines\\base.xml -c F:\\workspace\\all_server\\Base\\Source\\Gen\\Proto -t server -l cs"
},
"gen_base_proto_remote": {
"commandName": "Project",
"commandLineArgs": " -j proto -d F:\\workspace\\all_server\\ProtoDefines\\base.xml -c F:\\workspace\\all_server\\Base\\Source\\Gen\\Proto -t server -l cs"
},
"gen_base_db_remote": {
"commandName": "Project",
"commandLineArgs": " -j db -d F:\\workspace\\all_server\\DbDefines\\base.xml -c F:\\workspace\\all_server\\Base\\Source\\Gen\\Db -t server -l cs"
},
"gen_client_proto_lua": {
"commandName": "Project",
"commandLineArgs": "-h 127.0.0.1 -p 8899 -j proto -d F:\\workspace\\all_server\\ProtoDefines\\client.xml -c F:\\workspace\\x6p4\\X6Game\\Content\\Script\\Gen\\Proto -t client -l lua"
},
"gen_proto_lua_test": {
"commandName": "Project",
"commandLineArgs": "-h 127.0.0.1 -p 8899 -j proto -d ../../../../Test/root.proto.xml -c F:\\Gen\\Proto -t server -l lua"
},
"gen_client_proto_app_code_data": {
"commandName": "Project",
"commandLineArgs": "-h 127.0.0.1 -p 8899 -j config -d F:\\workspace\\perfect_gen_cs\\Test\\csv\\root.xml --outputappcodedir F:\\workspace\\all_server\\DemoClient\\Source\\Gen\\Cfg --outputappdatadir F:\\workspace\\all_server\\DemoClient\\Config -t server -l cs"
},
"gen_client_cfg_lua": {
"commandName": "Project",
"commandLineArgs": "-h 127.0.0.1 -p 8899 -j config -d D:\\NikkiP4_D\\DesignerConfig\\root.xml --outputappcodedir D:\\NikkiP4_D\\X6Game\\Content\\Script\\Gen\\Cfg --outputappdatadir D:\\NikkiP4_D\\X6Game\\Content\\Config -t server -l lua"
},
"gen_base_cfg_code_data": {
"commandName": "Project",
"commandLineArgs": "-h 127.0.0.1 -p 8899 -j config -d F:\\workspace\\perfect_gen_cs\\Test\\csv\\root.xml --outputappcodedir F:\\workspace\\all_server\\Base\\Source\\Gen\\Cfg --outputappdatadir F:\\workspace\\all_server\\Base\\Config -t server -l cs"
},
"gen_base_cfg_code_data_remote": {
"commandName": "Project",
"commandLineArgs": "-j config -d F:\\workspace\\perfect_gen_cs\\Test\\csv\\root.xml --outputappcodedir F:\\workspace\\all_server\\Base\\Source\\Gen\\Cfg --outputappdatadir F:\\workspace\\all_server\\Base\\Config -t server -l cs"
},
"gen_x6_base_db": {
"commandName": "Project",
"commandLineArgs": "-h 127.0.0.1 -p 8899 -j db -d D:\\NikkiP4_D\\X6Server\\DbDefines\\base.xml -c D:\\NikkiP4_D\\X6Server\\Online\\Source\\Gen\\Db -t server -l cs"
},
"gen_x6_base_proto": {
"commandName": "Project",
"commandLineArgs": " -j proto -d e:\\workspace\\x6_server\\ProtoDefines\\base.xml -c e:\\workspace\\x6_server\\GenShare\\Source\\Gen\\Proto --outputsynccodedir e:\\workspace\\x6_server\\Map\\Source\\Gen\\Objects -t server -l cs"
},
"gen_x6_client_proto": {
"commandName": "Project",
"commandLineArgs": "-h 127.0.0.1 -p 8899 -j proto -d D:\\workspace\\x6_server\\ProtoDefines\\client.xml -c D:\\NikkiP4_D\\X6Game\\Content\\Script\\Gen\\Proto --outputsynccodedir D:\\NikkiP4_D\\X6Game\\Content\\Script\\Gen\\Proto -t client -l lua"
},
"gen_x6_client_cfg_lua": {
"commandName": "Project",
"commandLineArgs": "-h 127.0.0.1 -p 8899 -j config -d D:\\NikkiP4_D\\X6Game\\DesignerConfigs\\root.xml --outputappcodedir D:\\NikkiP4_D\\X6Game\\Content\\Script\\Gen\\Cfg --outputappdatadir D:\\NikkiP4_D\\X6Game\\Content\\Config -t server -l lua"
},
"gen_cfg_export_debug": {
"commandName": "Project",
"commandLineArgs": "-h 127.0.0.1 -p 8899 -j config -d D:\\NikkiP4_D\\DesignerConfig\\root.xml --outputdatadir ./config -t server --outputdatatype json --exporttestdata"
}
}
}

View File

@ -0,0 +1,198 @@
using Bright.Net;
using Bright.Net.Bootstraps;
using Bright.Net.Channels;
using Bright.Net.Codecs;
using Bright.Net.ServiceModes.Managers;
using Bright.Time;
using Luban.Client.Common.Utils;
using Luban.Common.Protos;
using Luban.Common.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading.Tasks;
namespace Luban.Client.Common.Net
{
public class Session : SessionBase
{
public override void OnActive()
{
}
public override void OnInactive()
{
}
}
public class GenClient : ClientManager<Session>
{
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
public static GenClient Ins { get; } = new GenClient();
public async Task Start(string host, int port, Dictionary<int, ProtocolCreator> factories)
{
var c = new TcpClientBootstrap
{
RemoteAddress = new IPEndPoint(IPAddress.Parse(host), port),
ConnectTimeoutMills = 100,
EventLoop = new EventLoop(null),
InitHandler = ch =>
{
ch.Pipeline.AddLast(new ProtocolFrameCodec(20_000_000, new RecycleByteBufPool(100, 10), new DefaultProtocolAllocator(factories)));
ch.Pipeline.AddLast(this);
}
};
var ch = await c.ConnectAsync().ConfigureAwait(false);
}
public override void HandleProtocol(Protocol proto)
{
switch (proto.GetTypeId())
{
case GetInputFile.ID:
{
Task.Run(() => _ = OnGetInputFileAsync((GetInputFile)proto));
break;
}
case GetImportFileOrDirectory.ID:
{
Task.Run(() => _ = OnGetImportFileOrDirectoryAsync((GetImportFileOrDirectory)proto));
break;
}
case QueryFilesExists.ID:
{
Task.Run(() => Process((QueryFilesExists)proto));
break;
}
case PushLog.ID:
{
Process((PushLog)proto);
break;
}
case PushException.ID:
{
Process((PushException)proto);
break;
}
default:
{
s_logger.Error("unknown proto:{proto}", proto);
break;
}
}
}
private async Task OnGetImportFileOrDirectoryAsync(GetImportFileOrDirectory rpc)
{
long t1 = TimeUtil.NowMillis;
var file = rpc.Arg.FileOrDirName;
var re = new GetImportFileOrDirectoryRes()
{
SubFiles = new List<Luban.Common.Protos.FileInfo>(),
};
try
{
if (Directory.Exists(file))
{
re.Err = 0;
re.IsFile = false;
foreach (var subFile in Directory.GetFiles(file, "*", SearchOption.AllDirectories))
{
if (FileUtil.IsValidInputFile(subFile))
{
var md5 = await CacheMetaManager.Ins.GetOrUpdateFileMd5Async(subFile);
re.SubFiles.Add(new Luban.Common.Protos.FileInfo() { FilePath = FileUtil.Standardize(subFile), MD5 = md5 });
}
}
}
else if (File.Exists(file))
{
re.IsFile = true;
re.Md5 = await CacheMetaManager.Ins.GetOrUpdateFileMd5Async(file);
}
else
{
re.Err = Luban.Common.EErrorCode.FILE_OR_DIR_NOT_EXISTS;
}
}
catch (Exception e)
{
re.Err = Luban.Common.EErrorCode.READ_FILE_FAIL;
s_logger.Error(e);
}
s_logger.Trace(" GetImportFileOrDirectory file:{file} err:{err} cost:{time}", file, re.Err, TimeUtil.NowMillis - t1);
Session.ReplyRpc<GetImportFileOrDirectory, GetImportFileOrDirectoryArg, GetImportFileOrDirectoryRes>(rpc, re);
}
private async Task OnGetInputFileAsync(GetInputFile rpc)
{
long t1 = TimeUtil.NowMillis;
var res = new GetInputFileRes() { Err = Luban.Common.EErrorCode.OK };
try
{
res.Content = await FileUtil.ReadAllBytesAsync(rpc.Arg.File);
//res.Content = FileUtil.ReadAllBytes(rpc.Arg.File);
res.Err = Luban.Common.EErrorCode.OK;
}
catch (Exception)
{
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);
Session.ReplyRpc<GetInputFile, GetInputFileArg, GetInputFileRes>(rpc, res);
}
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) };
foreach (var f in files)
{
re.Exists.Add(File.Exists(Path.Combine(root, f)));
}
Session.ReplyRpc<QueryFilesExists, QueryFilesExistsArg, QueryFilesExistsRes>(p, re);
}
private void Process(PushLog p)
{
switch (p.Level)
{
case "trace":
{
s_logger.Trace(p.LogContent);
break;
}
case "info":
{
s_logger.Info(p.LogContent);
break;
}
default:
{
s_logger.Error(p.LogContent);
break;
}
}
}
private void Process(PushException p)
{
s_logger.Error(p.LogContent);
s_logger.Error(p.StackTrace);
}
}
}

View File

@ -0,0 +1,217 @@
using Luban.Client.Common.Net;
using Luban.Client.Common.Utils;
using Luban.Common.Protos;
using Luban.Common.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Luban.Client
{
class Program
{
public class CommandLineOptions
{
public string Host { get; set; }
public int Port { get; set; } = 8899;
public string JobType { get; set; }
public List<string> JobArguments { get; set; } = new List<string>();
public bool Verbose { get; set; }
public string CacheMetaInfoFile { get; set; } = ".cache.meta";
}
private static NLog.Logger s_logger;
private static void PrintUsage(string err)
{
Console.WriteLine("ERRORS:");
Console.WriteLine("\t" + err);
Console.WriteLine(@"
Luban.Client <Options>... [-- [job options]]
e.g.
Luban.Client -h 127.0.0.1 -p 2234 -j cfg -- --name abc
Options:
-h, --host Required. host ip
-p --port port. default 8899
-j --job Required. job type. avaliable value: cfg
-v --verbose verbose print
-c --cachemetafile cache meta file name
-h --help show usage
");
}
private static (object, CommandLineOptions) ParseArgs(string[] args)
{
var ops = new CommandLineOptions();
for (int i = 0; i < args.Length; i++)
{
var arg = args[i];
try
{
switch (arg)
{
case "-h":
case "--host":
{
ops.Host = args[++i];
break;
}
case "-p":
case "--port":
{
ops.Port = int.Parse(args[++i]);
break;
}
case "-j":
case "--job":
{
ops.JobType = args[++i];
break;
}
case "-v":
case "--verbose":
{
ops.Verbose = true;
break;
}
case "-c":
case "--cachemetafile":
{
ops.CacheMetaInfoFile = args[++i];
break;
}
case "--":
{
++i;
ops.JobArguments = args.ToList().GetRange(i, args.Length - i);
return (null, ops);
}
default:
{
return ($"unknown argument:{arg}", null);
}
}
}
catch (Exception)
{
return ($"argument:{arg} err", null);
}
}
if (ops.Host == null)
{
return ("--host missing", null);
}
if (ops.JobType == null)
{
return ("--job missing", null);
}
return (null, ops);
}
static void Main(string[] args)
{
var profile = new ProfileTimer();
profile.StartPhase("all");
var parseResult = ParseArgs(args);
if (parseResult.Item1 != null)
{
PrintUsage((string)parseResult.Item1);
Environment.Exit(1);
return;
}
CommandLineOptions options = parseResult.Item2;
profile.StartPhase("init logger");
LogUtil.InitSimpleNLogConfigure(NLog.LogLevel.Info);
s_logger = NLog.LogManager.GetCurrentClassLogger();
profile.EndPhaseAndLog();
ThreadPool.SetMinThreads(4, 5);
ThreadPool.SetMaxThreads(64, 10);
int exitCode;
try
{
profile.StartPhase("load cache meta file");
CacheMetaManager.Ins.Load(options.CacheMetaInfoFile);
profile.EndPhaseAndLog();
profile.StartPhase("connect server");
var conn = GenClient.Ins.Start(options.Host, options.Port, ProtocolStub.Factories);
conn.Wait();
profile.EndPhaseAndLog();
profile.StartPhase("gen job");
exitCode = SubmitGenJob(options);
profile.EndPhaseAndLog();
}
catch (Exception e)
{
exitCode = 1;
s_logger.Error(e);
}
CacheMetaManager.Ins.Save();
profile.EndPhaseAndLog();
if (exitCode == 0)
{
s_logger.Info("== succ ==");
}
else
{
s_logger.Error("== fail ==");
}
Environment.Exit(exitCode);
}
const int GEN_JOB_TIMEOUT = 30;
private static int SubmitGenJob(CommandLineOptions options)
{
var res = GenClient.Ins.Session.CallRpcAsync<GenJob, GenJobArg, GenJobRes>(new GenJobArg()
{
JobType = options.JobType,
JobArguments = options.JobArguments,
Verbose = options.Verbose,
}, GEN_JOB_TIMEOUT).Result;
if (res.ErrCode != 0)
{
if (res.ErrCode == Luban.Common.EErrorCode.JOB_ARGUMENT_ERROR)
{
s_logger.Error("job argument error");
Console.WriteLine(res.ErrMsg);
}
else
{
s_logger.Error("GenJob fail. err:{err} msg:{msg}", res.ErrCode, res.ErrMsg);
}
return 1;
}
var tasks = new List<Task>();
foreach (var fg in res.FileGroups)
{
tasks.Add(DownloadFileUtil.DownloadGeneratedFiles(fg.Dir, fg.Files));
}
Task.WaitAll(tasks.ToArray());
return 0;
}
}
}

View File

@ -0,0 +1,213 @@
using Luban.Common.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Luban.Client.Common.Utils
{
public class CacheMetaManager
{
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
public static CacheMetaManager Ins { get; } = new CacheMetaManager();
class MetaInfo
{
public string FullPath { get; set; }
public string Md5 { get; set; }
public long FileLength { get; set; }
public long FileModifiedTime { get; set; }
public bool Visited { get; set; }
}
private readonly object _lock = new object();
private string _metaFile;
private volatile bool _dirty;
private readonly SortedDictionary<string, MetaInfo> _cacheFileMetas = new SortedDictionary<string, MetaInfo>();
public void Load(string metaFile)
{
_metaFile = metaFile;
_dirty = false;
try
{
if (File.Exists(metaFile))
{
foreach (string line in File.ReadAllLines(metaFile, Encoding.UTF8))
{
if (string.IsNullOrWhiteSpace(line))
{
continue;
}
string[] args = line.Split(',');
if (args.Length != 4)
{
_dirty = true;
s_logger.Error("corrupt line:{line}", line);
continue;
}
var fullPath = args[0];
string md5 = args[1];
long fileLength = long.Parse(args[2]);
long fileModifiedTime = long.Parse(args[3]);
if (!File.Exists(fullPath))
{
_dirty = true;
s_logger.Debug("[drop] cache file:{file} not exist.", fullPath);
continue;
}
var fileInfo = new FileInfo(fullPath);
long actualFileLength = fileInfo.Length;
if (actualFileLength != fileLength)
{
_dirty = true;
s_logger.Debug("[drop] cache file:{file} length not match. cache length:{cl} actual length:{al}", fullPath, fileLength, actualFileLength);
continue;
}
long actualLastModifiedTime = ((DateTimeOffset)fileInfo.LastWriteTime).ToUnixTimeMilliseconds();
if (actualLastModifiedTime != fileModifiedTime)
{
_dirty = true;
s_logger.Debug("[drop] cache file:{file} last modified time not match. cache:{cl} actual:{al}", fullPath, fileModifiedTime, actualLastModifiedTime);
continue;
}
_cacheFileMetas[fullPath] = new MetaInfo
{
FullPath = fullPath,
Md5 = md5,
FileLength = fileLength,
FileModifiedTime = fileModifiedTime,
};
s_logger.Debug("load cache. file:{file} md5:{md5} length:{length} last modified time:{time}", fullPath, md5, fileLength, fileModifiedTime);
}
}
else
{
s_logger.Info("meta file:{meta} not exist. ignore load", metaFile);
}
}
catch (Exception e)
{
s_logger.Error(e, "load meta file fail");
}
}
public void Save()
{
if (!_dirty)
{
return;
}
lock (_lock)
{
_dirty = false;
var content = _cacheFileMetas.Values.Select(meta => $"{meta.FullPath},{meta.Md5},{meta.FileLength},{meta.FileModifiedTime}");
File.WriteAllLines(_metaFile, content, Encoding.UTF8);
}
s_logger.Info("[Save] meta file:{metaFile} updated!", _metaFile);
}
private MetaInfo GetMetaInfo(string file)
{
lock (_lock)
{
var fullPath = Path.GetFullPath(file).Replace('\\', '/');
return _cacheFileMetas.TryGetValue(fullPath, out var meta) ? meta : null;
}
}
private static async Task<MetaInfo> BuildMetaInfo(string file, string md5 = null)
{
var fullPath = Path.GetFullPath(file).Replace('\\', '/');
if (md5 == null)
{
s_logger.Info("comput md5. file:{file}", file);
md5 = FileUtil.CalcMD5(await FileUtil.ReadAllBytesAsync(file));
}
var fileInfo = new FileInfo(fullPath);
long actualFileLength = fileInfo.Length;
long actualLastModifiedTime = ((DateTimeOffset)fileInfo.LastWriteTime).ToUnixTimeMilliseconds();
return new MetaInfo()
{
FullPath = fullPath,
Md5 = md5,
FileLength = actualFileLength,
FileModifiedTime = actualLastModifiedTime,
};
}
public async Task<string> GetOrUpdateFileMd5Async(string file)
{
var meta = GetMetaInfo(file);
if (meta == null)
{
meta = await BuildMetaInfo(file);
lock (_lock)
{
_dirty = true;
_cacheFileMetas.Add(meta.FullPath, meta);
}
s_logger.Debug("[add] meta not find, build it. file:{file} path:{path} md5:{md5} length:{length}", file, meta.FullPath, meta.Md5, meta.FileLength);
}
else
{
s_logger.Debug("[cache hit] file:{file} path:{path} md5:{md5} length:{length}", file, meta.FullPath, meta.Md5, meta.FileLength);
}
return meta.Md5;
}
public async Task<bool> CheckFileChangeAsync(string relateDir, string filePath, string md5)
{
var outputPath = relateDir != null ? FileUtil.Combine(relateDir, filePath) : filePath;
var meta = GetMetaInfo(outputPath);
if (meta == null)
{
if (!File.Exists(outputPath))
{
return true;
}
meta = await BuildMetaInfo(outputPath);
lock (_lock)
{
_dirty = true;
_cacheFileMetas.Add(meta.FullPath, meta);
}
s_logger.Debug("[add] meta not find, create it. file:{file} path:{path} md5:{md5} length:{length}", outputPath, meta.FullPath, meta.Md5, meta.FileLength);
}
if (meta.Md5 != md5)
{
s_logger.Debug("[add] meta md5 not match, file:{file} path:{path} md5:{md5} length:{length}", outputPath, meta.FullPath, meta.Md5, meta.FileLength);
return true;
}
return false;
}
public async Task UpdateFileAsync(string relateDir, string filePath, string md5)
{
var file = relateDir != null ? FileUtil.Combine(relateDir, filePath) : filePath;
var meta = await BuildMetaInfo(file, md5);
lock (_lock)
{
_dirty = true;
_cacheFileMetas[meta.FullPath] = meta;
}
s_logger.Debug("[update] file:{file} path:{path} md5:{md5} length:{length}", file, meta.FullPath, meta.Md5, meta.FileLength);
}
}
}

View File

@ -0,0 +1,58 @@
using Luban.Client.Common.Net;
using Luban.Common.Protos;
using Luban.Common.Utils;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Luban.Client.Common.Utils
{
public static class DownloadFileUtil
{
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
const int DOWNLOAD_TIMTOUT = 10;
public static async Task DownloadGeneratedFiles(string outputDir, List<FileInfo> newFiles)
{
List<Task> tasks = new List<Task>();
foreach (var file in newFiles)
{
if (!await CacheMetaManager.Ins.CheckFileChangeAsync(outputDir, file.FilePath, file.MD5))
{
continue;
}
tasks.Add(Task.Run(async () =>
{
s_logger.Trace("new code file:{@file}", file);
GetOutputFileRes res = await GenClient.Ins.Session.CallRpcAsync<GetOutputFile, GetOutputFileArg, GetOutputFileRes>(new GetOutputFileArg()
{
MD5 = file.MD5,
}, DOWNLOAD_TIMTOUT);
await FileUtil.SaveFileAsync(outputDir, file.FilePath, res.FileContent);
await CacheMetaManager.Ins.UpdateFileAsync(outputDir, file.FilePath, file.MD5);
}));
}
await Task.WhenAll(tasks);
// todo 感觉有点问题哈不是每个生成目录都需要clean up 的
FileCleaner.Clean(outputDir, newFiles);
}
public static async Task DownloadGeneratedFile(FileInfo file)
{
if (!await CacheMetaManager.Ins.CheckFileChangeAsync(null, file.FilePath, file.MD5))
{
return;
}
GetOutputFileRes res = await GenClient.Ins.Session.CallRpcAsync<GetOutputFile, GetOutputFileArg, GetOutputFileRes>(new GetOutputFileArg()
{
MD5 = file.MD5,
}, DOWNLOAD_TIMTOUT).ConfigureAwait(false);
await FileUtil.SaveFileAsync(null, file.FilePath, res.FileContent);
await CacheMetaManager.Ins.UpdateFileAsync(null, file.FilePath, file.MD5);
}
}
}

View File

@ -0,0 +1,110 @@
using System.Collections.Generic;
using System.IO;
namespace Luban.Client.Common.Utils
{
class FileCleaner
{
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
private readonly HashSet<string> _outputDirs = new HashSet<string>();
private readonly HashSet<string> _savedFileOrDirs = new HashSet<string>();
private readonly HashSet<string> _ignoreFileExtensions = new HashSet<string>();
public void AddIgnoreExtension(string ext)
{
_ignoreFileExtensions.Add(ext);
}
public void AddOutputDir(string dir)
{
_outputDirs.Add(dir);
}
public void AddSavedFile(string file)
{
file = file.Replace('\\', '/');
while (true)
{
_savedFileOrDirs.Add(file);
s_logger.Trace("add save file:{file}", file);
int sepIndex = file.LastIndexOf('/');
if (sepIndex < 0)
{
break;
}
file = file[..sepIndex];
}
}
public void RemoveUnusedFiles()
{
foreach (var dir in _outputDirs)
{
RemoveUnusedFileInDir(dir);
}
}
private void RemoveUnusedFileInDir(string dir)
{
if (!Directory.Exists(dir))
{
return;
}
var fullRootPath = Path.GetFullPath(dir);
s_logger.Trace("full path:{path}", fullRootPath);
foreach (var file in Directory.GetFiles(dir, "*", SearchOption.AllDirectories))
{
s_logger.Trace("file:{file}", file);
string fullSubFilePath = Path.GetFullPath(file);
var relateFile = fullSubFilePath[(fullRootPath.Length + 1)..].Replace('\\', '/');
if (_savedFileOrDirs.Contains(relateFile))
{
s_logger.Trace("remain file:{file}", file);
}
else
{
s_logger.Info("[remove] file: {file}", file);
File.Delete(file);
}
}
// 清除空目录
var subDirs = new List<string>(Directory.GetDirectories(dir, "*", SearchOption.AllDirectories));
subDirs.Sort((a, b) => -a.CompareTo(b));
foreach (var subDir in subDirs)
{
string fullSubDirPath = Path.GetFullPath(subDir);
var relateDir = fullSubDirPath[(fullRootPath.Length + 1)..].Replace('\\', '/');
if (_savedFileOrDirs.Contains(relateDir))
{
s_logger.Trace("remain directory:{dir}", relateDir);
}
else
{
s_logger.Info("[remove] dir: {dir}", subDir);
Directory.Delete(subDir);
}
}
}
public static void Clean(string outputDir, List<Luban.Common.Protos.FileInfo> savedFiles)
{
var cleaner = new FileCleaner();
cleaner.AddOutputDir(outputDir);
cleaner.AddIgnoreExtension("meta"); // for unity
foreach (var file in savedFiles)
{
cleaner.AddSavedFile(file.FilePath);
}
cleaner.RemoveUnusedFiles();
}
}
}

View File

@ -0,0 +1,25 @@
[*.cs]
# CA1707: 标识符不应包含下划线
dotnet_diagnostic.CA1707.severity = none
# CA1305: 指定 IFormatProvider
dotnet_diagnostic.CA1305.severity = none
# CA1303: 请不要将文本作为本地化参数传递
dotnet_diagnostic.CA1303.severity = none
# CA1062: 验证公共方法的参数
dotnet_diagnostic.CA1062.severity = none
# CA2227: 集合属性应为只读
dotnet_diagnostic.CA2227.severity = none
# CA1819: 属性不应返回数组
dotnet_diagnostic.CA1819.severity = none
# CA1304: 指定 CultureInfo
dotnet_diagnostic.CA1304.severity = none
# CA1031: 不捕获常规异常类型
dotnet_diagnostic.CA1031.severity = none

View File

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Bright.Net" Version="1.1.0.41" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,12 @@
namespace Luban.Common
{
public enum EErrorCode
{
OK,
UNKNOWN_JOB_TYPE,
FILE_OR_DIR_NOT_EXISTS,
READ_FILE_FAIL,
JOB_ARGUMENT_ERROR,
JOB_EXCEPTION,
}
}

View File

@ -0,0 +1,28 @@
using Bright.Serialization;
namespace Luban.Common.Protos
{
public class FileInfo : BeanBase
{
public string FilePath { get; set; }
public string MD5 { get; set; }
public override void Serialize(ByteBuf os)
{
os.WriteString(FilePath);
os.WriteString(MD5);
}
public override void Deserialize(ByteBuf os)
{
FilePath = os.ReadString();
MD5 = os.ReadString();
}
public override int GetTypeId()
{
throw new System.NotImplementedException();
}
}
}

View File

@ -0,0 +1,109 @@
using Bright.Net.Codecs;
using Bright.Serialization;
using System;
using System.Collections.Generic;
namespace Luban.Common.Protos
{
public class GenJobArg : BeanBase
{
public string JobType { get; set; }
public List<string> JobArguments { get; set; }
public bool Verbose { get; set; }
public override int GetTypeId()
{
return 0;
}
public override void Serialize(ByteBuf os)
{
os.WriteString(JobType);
Bright.Common.SerializationUtil.Serialize(os, JobArguments);
os.WriteBool(Verbose);
}
public override void Deserialize(ByteBuf os)
{
JobType = os.ReadString();
Bright.Common.SerializationUtil.Deserialize(os, JobArguments = new List<string>());
Verbose = os.ReadBool();
}
}
public class FileGroup : BeanBase
{
public string Dir { get; set; }
public List<FileInfo> Files { get; set; }
public override int GetTypeId()
{
return 0;
}
public override void Serialize(ByteBuf os)
{
os.WriteString(Dir);
Bright.Common.SerializationUtil.Serialize(os, Files);
}
public override void Deserialize(ByteBuf os)
{
Dir = os.ReadString();
Bright.Common.SerializationUtil.Deserialize(os, Files = new List<FileInfo>());
}
}
public class GenJobRes : BeanBase
{
public EErrorCode ErrCode { get; set; }
public string ErrMsg { get; set; }
public List<FileGroup> FileGroups { get; set; }
public override int GetTypeId()
{
return 0;
}
public override void Serialize(ByteBuf os)
{
os.WriteInt((int)ErrCode);
os.WriteString(ErrMsg);
Bright.Common.SerializationUtil.Serialize(os, FileGroups);
}
public override void Deserialize(ByteBuf os)
{
ErrCode = (EErrorCode)os.ReadInt();
ErrMsg = os.ReadString();
Bright.Common.SerializationUtil.Deserialize(os, FileGroups = new List<FileGroup>());
}
}
public class GenJob : Rpc<GenJobArg, GenJobRes>
{
public const int ID = 100;
public override int GetTypeId()
{
return ID;
}
public override object Clone()
{
throw new NotImplementedException();
}
public override void Reset()
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,79 @@
using Bright.Net.Codecs;
using Bright.Serialization;
using System;
using System.Collections.Generic;
namespace Luban.Common.Protos
{
public class GetImportFileOrDirectoryArg : BeanBase
{
public string FileOrDirName { get; set; }
public override int GetTypeId()
{
return 0;
}
public override void Serialize(ByteBuf os)
{
os.WriteString(FileOrDirName);
}
public override void Deserialize(ByteBuf os)
{
FileOrDirName = os.ReadString();
}
}
public class GetImportFileOrDirectoryRes : BeanBase
{
public EErrorCode Err { get; set; }
public bool IsFile { get; set; }
public string Md5 { get; set; }
//public byte[] Content { get; set; }
public List<FileInfo> SubFiles { get; set; }
public override int GetTypeId()
{
return 0;
}
public override void Serialize(ByteBuf os)
{
os.WriteInt((int)Err);
os.WriteBool(IsFile);
os.WriteString(Md5);
Bright.Common.SerializationUtil.Serialize(os, SubFiles);
}
public override void Deserialize(ByteBuf os)
{
Err = (EErrorCode)os.ReadInt();
IsFile = os.ReadBool();
Md5 = os.ReadString();
Bright.Common.SerializationUtil.Deserialize(os, SubFiles = new List<FileInfo>());
}
}
public class GetImportFileOrDirectory : Rpc<GetImportFileOrDirectoryArg, GetImportFileOrDirectoryRes>
{
public const int ID = 108;
public override int GetTypeId()
{
return ID;
}
public override void Reset()
{
throw new NotImplementedException();
}
public override object Clone()
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,71 @@
using Bright.Net.Codecs;
using Bright.Serialization;
namespace Luban.Common.Protos
{
public class GetInputFileArg : BeanBase
{
public string File { get; set; }
public override void Serialize(ByteBuf os)
{
os.WriteString(File);
}
public override void Deserialize(ByteBuf os)
{
File = os.ReadString();
}
public override int GetTypeId()
{
throw new System.NotImplementedException();
}
}
public class GetInputFileRes : BeanBase
{
public EErrorCode Err { get; set; }
public byte[] Content { get; set; }
public override void Serialize(ByteBuf os)
{
os.WriteInt((int)Err);
os.WriteBytes(Content);
}
public override void Deserialize(ByteBuf os)
{
Err = (EErrorCode)os.ReadInt();
Content = os.ReadBytes();
}
public override int GetTypeId()
{
throw new System.NotImplementedException();
}
}
public class GetInputFile : Rpc<GetInputFileArg, GetInputFileRes>
{
public const int ID = 102;
public override int GetTypeId()
{
return ID;
}
public override void Reset()
{
throw new System.NotImplementedException();
}
public override object Clone()
{
throw new System.NotImplementedException();
}
}
}

View File

@ -0,0 +1,80 @@
using Bright.Net.Codecs;
using Bright.Serialization;
namespace Luban.Common.Protos
{
public class GetOutputFile : Rpc<GetOutputFileArg, GetOutputFileRes>
{
public const int ID = 103;
public override int GetTypeId()
{
return ID;
}
public override void Reset()
{
throw new System.NotImplementedException();
}
public override object Clone()
{
throw new System.NotImplementedException();
}
}
public class GetOutputFileArg : BeanBase
{
//public string Type { get; set; }
//public string RelatePath { get; set; }
public string MD5 { get; set; }
public override int GetTypeId()
{
throw new System.NotImplementedException();
}
public override void Serialize(ByteBuf os)
{
//os.WriteString(Type);
//os.WriteString(RelatePath);
os.WriteString(MD5);
}
public override void Deserialize(ByteBuf os)
{
//Type = os.ReadString();
//RelatePath = os.ReadString();
MD5 = os.ReadString();
}
}
public class GetOutputFileRes : BeanBase
{
public bool Exists { get; set; }
public byte[] FileContent { get; set; }
public override void Serialize(ByteBuf os)
{
os.WriteBool(Exists);
os.WriteBytes(FileContent);
}
public override void Deserialize(ByteBuf os)
{
Exists = os.ReadBool();
FileContent = os.ReadBytes();
}
public override int GetTypeId()
{
throw new System.NotImplementedException();
}
}
}

View File

@ -0,0 +1,19 @@
using Bright.Net.Codecs;
using System.Collections.Generic;
namespace Luban.Common.Protos
{
public static class ProtocolStub
{
public static Dictionary<int, ProtocolCreator> Factories { get; } = new Dictionary<int, ProtocolCreator>
{
[GetInputFile.ID] = () => new GetInputFile(),
[GetOutputFile.ID] = () => new GetOutputFile(),
[PushLog.ID] = () => new PushLog(),
[PushException.ID] = () => new PushException(),
[GenJob.ID] = () => new GenJob(),
[GetImportFileOrDirectory.ID] = () => new GetImportFileOrDirectory(),
[QueryFilesExists.ID] = () => new QueryFilesExists(),
};
}
}

View File

@ -0,0 +1,44 @@
using Bright.Net.Codecs;
using Bright.Serialization;
namespace Luban.Common.Protos
{
public class PushException : Protocol
{
public const int ID = 105;
public override int GetTypeId()
{
return ID;
}
public string LogContent { get; set; }
public string Message { get; set; }
public string StackTrace { get; set; }
public override void Serialize(ByteBuf os)
{
os.WriteString(LogContent);
os.WriteString(Message);
os.WriteString(StackTrace);
}
public override void Deserialize(ByteBuf os)
{
LogContent = os.ReadString();
Message = os.ReadString();
StackTrace = os.ReadString();
}
public override object Clone()
{
throw new System.NotImplementedException();
}
public override void Reset()
{
throw new System.NotImplementedException();
}
}
}

View File

@ -0,0 +1,41 @@
using Bright.Net.Codecs;
using Bright.Serialization;
namespace Luban.Common.Protos
{
public class PushLog : Protocol
{
public const int ID = 104;
public override int GetTypeId()
{
return ID;
}
public string Level { get; set; }
public string LogContent { get; set; }
public override void Serialize(ByteBuf os)
{
os.WriteString(Level);
os.WriteString(LogContent);
}
public override void Deserialize(ByteBuf os)
{
Level = os.ReadString();
LogContent = os.ReadString();
}
public override object Clone()
{
throw new System.NotImplementedException();
}
public override void Reset()
{
throw new System.NotImplementedException();
}
}
}

View File

@ -0,0 +1,78 @@
using Bright.Net.Codecs;
using Bright.Serialization;
using System;
using System.Collections.Generic;
namespace Luban.Common.Protos
{
public class QueryFilesExistsArg : BeanBase
{
public string Root { get; set; }
public List<string> Files { get; set; }
public override int GetTypeId()
{
return 0;
}
public override void Serialize(ByteBuf os)
{
os.WriteString(Root);
Bright.Common.SerializationUtil.Serialize(os, Files);
}
public override void Deserialize(ByteBuf os)
{
Root = os.ReadString();
Bright.Common.SerializationUtil.Deserialize(os, Files = new List<string>());
}
}
public class QueryFilesExistsRes : BeanBase
{
public List<bool> Exists { get; set; }
public override int GetTypeId()
{
return 0;
}
public override void Serialize(ByteBuf os)
{
os.WriteSize(Exists.Count);
foreach (var v in Exists)
{
os.WriteBool(v);
}
}
public override void Deserialize(ByteBuf os)
{
int n = os.ReadSize();
Exists = new List<bool>();
for (int i = 0; i < n; i++)
{
Exists.Add(os.ReadBool());
}
}
}
public class QueryFilesExists : Rpc<QueryFilesExistsArg, QueryFilesExistsRes>
{
public const int ID = 106;
public override int GetTypeId()
{
return ID;
}
public override object Clone()
{
throw new NotImplementedException();
}
public override void Reset()
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,145 @@
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace Luban.Common.Utils
{
public static class FileUtil
{
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
public static string GetFileName(string path)
{
int index = path.Replace('\\', '/').LastIndexOf('/');
return index >= 0 ? path[(index + 1)..] : path;
}
public static string GetParent(string path)
{
int index = path.Replace('\\', '/').LastIndexOf('/');
return index >= 0 ? path[..index] : path;
}
public static string GetPathRelateRootFile(string rootFile, string file)
{
return Combine(GetParent(rootFile), file);
}
/// <summary>
/// 忽略以 文件名以 '.' '_' '~' 开头的文件
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public static bool IsValidInputFile(string file)
{
if (!File.Exists(file))
{
return false;
}
var f = new FileInfo(file);
string fname = f.Name;
return !fname.StartsWith('.') && !fname.StartsWith('_') && !fname.StartsWith('~');
}
[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)
{
using MD5 md5 = MD5.Create();
var md5Bytes = md5.ComputeHash(srcBytes);
var s = new StringBuilder(md5Bytes.Length * 2);
foreach (var b in md5Bytes)
{
s.Append(b.ToString("X"));
}
return s.ToString();
}
public static string Standardize(string path)
{
return path.Replace('\\', '/');
}
public static string Combine(string parent, string sub)
{
return Standardize(Path.Combine(parent, sub));
}
public static async Task SaveFileAsync(string relateDir, string filePath, byte[] content)
{
// 调用此接口时,已保证 文件必然是改变的,不用再检查对比文件
var outputPath = Standardize(relateDir != null ? System.IO.Path.Combine(relateDir, filePath) : filePath);
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))
{
File.SetAttributes(outputPath, FileAttributes.Normal);
}
}
else
{
s_logger.Info("[new] {file}", outputPath);
}
await File.WriteAllBytesAsync(outputPath, content);
}
public static async Task<byte[]> ReadAllBytesAsync(string file)
{
// File.ReadAllBytesAsync 无法读取被打开的excel文件只好重新实现了一个
using var fs = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
long count = fs.Length;
var bytes = new byte[count];
int writeIndex = 0;
while (writeIndex < count)
{
int n = await fs.ReadAsync(bytes, writeIndex, (int)count - writeIndex, default);
writeIndex += n;
}
return bytes;
}
public static byte[] ReadAllBytes(string file)
{
// File.ReadAllBytesAsync 无法读取被打开的excel文件只好重新实现了一个
using var fs = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
long count = fs.Length;
var bytes = new byte[count];
int writeIndex = 0;
while (writeIndex < count)
{
int n = fs.Read(bytes, writeIndex, (int)count - writeIndex);
writeIndex += n;
}
return bytes;
}
}
}

View File

@ -0,0 +1,15 @@
namespace Luban.Common.Utils
{
public static class LogUtil
{
public static void InitSimpleNLogConfigure(NLog.LogLevel minConsoleLogLevel)
{
var logConfig = new NLog.Config.LoggingConfiguration();
//var layout = NLog.Layouts.Layout.FromString("${longdate}|${level:uppercase=true}|${threadid}|${message}${onexception:${newline}${exception:format=tostring}${exception:format=StackTrace}}");
var 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.Fatal, "console");
NLog.LogManager.Configuration = logConfig;
}
}
}

View File

@ -0,0 +1,43 @@
using Bright.Time;
using System.Collections.Generic;
namespace Luban.Common.Utils
{
public class Phase
{
public string Name { get; set; }
public long StartTime { get; set; }
public long EndTime { get; set; }
public long ElapseTime { get; set; }
}
public class ProfileTimer
{
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
private readonly Stack<Phase> phaseStack = new Stack<Phase>();
public void StartPhase(string name)
{
phaseStack.Push(new Phase() { Name = name, StartTime = TimeUtil.NowMillis });
}
private Phase EndPhase()
{
var phase = phaseStack.Pop();
phase.EndTime = TimeUtil.NowMillis;
phase.ElapseTime = phase.EndTime - phase.StartTime;
return phase;
}
public Phase EndPhaseAndLog()
{
var phase = EndPhase();
s_logger.Info("====== {name} cost {time} ms ======", phase.Name, phase.ElapseTime);
return phase;
}
}
}

View File

@ -0,0 +1,207 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Luban.Common.Utils
{
public static class TypeUtil
{
public static (string, string) SplitFullName(string fullName)
{
int index = fullName.LastIndexOf('.');
return (fullName.Substring(0, index), fullName.Substring(index + 1));
}
public static string MakeFullName(Stack<object> path)
{
var reverse = new List<string>();
int index = 0;
foreach (var e in path.Reverse())
{
if (!(e is string))
{
reverse.Add("[" + e + "]");
}
else
{
// 索引 0 是 table名 不应该加 .
if (index >= 1)
{
reverse.Add(".");
}
reverse.Add(e.ToString());
}
++index;
}
return string.Join("", reverse);
}
public static string MakeCppNamespaceBegin(string module)
{
return string.Join("", module.Split('.').Select(n => $"namespace {n} {{"));
}
public static string MakeCppNamespaceEnd(string module)
{
return string.Join("", module.Split('.').Select(n => $"}}"));
}
public static string MakeCppFullName(string module, string name)
{
return string.Join("::", MakeFullName(module, name).Split('.'));
}
public static string MakeCppJoinedFullName(string module, string ueName)
{
return string.Join("", module.Split('.').Select(n => $"{n}_")) + ueName;
}
public static string MakeFullName(string module, string name)
{
return module != null && module.Length > 0 ? module + "." + name : name;
}
public static string MakeGoPkgName(string module)
{
int index = module.LastIndexOf('.');
return index >= 0 ? module.Substring(index + 1) : module;
}
public static string MakeGoNamespace(string module)
{
return string.Join("", module.Split('.').Where(s => !string.IsNullOrWhiteSpace(s)).Select(s => UpperCaseFirstChar(s)));
}
public static string MakeGoFullName(string module, string name)
{
return MakeGoNamespace(module) + "_" + name;
}
public static string MakePyFullName(string module, string name)
{
return module.Replace('.', '_') + "_" + name;
}
public static string MakeNamespace(string module, string subModule)
{
if (module.Length == 0)
{
return subModule;
}
if (subModule.Length == 0)
{
return module;
}
return module + "." + subModule;
}
/// <summary>
/// the return value in range [offset, 2^14)
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public static uint ComputProtoHashIdByName(string name)
{
uint id = 0;
foreach (char c in name)
{
id = 31 * id + c;
}
uint maxId = 2 << 14;
uint offset = 1000;
return id % (maxId - offset) + offset;
}
public static int ComputCfgHashIdByName(string name)
{
int id = 0;
foreach (char c in name)
{
id = 31 * id + c;
}
return id;
}
private static readonly HashSet<string> s_reserveNames = new HashSet<string>()
{
"end",
"base",
"super",
"const",
"is",
"as",
"typeid",
"typeof",
"object",
"ref",
"out",
"in",
"os",
"sb",
"ele",
"new",
"friend",
"public",
"protected",
"private",
"internal",
"return",
"static",
};
public static bool IsValidName(string name)
{
name = name.Trim();
return name.Length > 0 && !s_reserveNames.Contains(name);
}
public static string UpperCaseFirstChar(string s)
{
return char.ToUpper(s[0]) + s.Substring(1);
}
public static string ToCsStyleName(string orginName)
{
return string.Join("", orginName.Split('_').Select(c => UpperCaseFirstChar(c)));
}
public static string ToJavaStyleName(string orginName)
{
var words = orginName.Split('_');
var s = new StringBuilder();
s.Append(words[0]);
for (int i = 1; i < words.Length; i++)
{
s.Append(UpperCaseFirstChar(words[i]));
}
return s.ToString();
}
public static string ToJavaGetterName(string orginName)
{
var words = orginName.Split('_');
var s = new StringBuilder("get");
foreach (var word in words)
{
s.Append(UpperCaseFirstChar(word));
}
return s.ToString();
}
public static string GetNamespace(string fullName)
{
var index = fullName.LastIndexOf('.');
return index >= 0 ? fullName.Substring(0, index) : "";
}
public static string GetName(string fullName)
{
var index = fullName.LastIndexOf('.');
return index >= 0 ? fullName.Substring(index + 1, fullName.Length - index - 1) : fullName;
}
}
}

View File

@ -0,0 +1,112 @@
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Xml.Linq;
namespace Luban.Common.Utils
{
public class LoadXmlException : Exception
{
public LoadXmlException()
{
}
public LoadXmlException(string message) : base(message)
{
}
public LoadXmlException(string message, Exception innerException) : base(message, innerException)
{
}
protected LoadXmlException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
public static class XmlUtil
{
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
public static XElement Open(string xmlFile)
{
try
{
s_logger.Trace("open {xml}", xmlFile);
return XElement.Load(xmlFile);
}
catch (Exception e)
{
throw new LoadXmlException($"打开定义文件:{xmlFile} 失败 --> {e.Message}");
}
}
public static XElement Open(string xmlFile, byte[] content)
{
try
{
s_logger.Trace("open {xml}", xmlFile);
return XElement.Load(new MemoryStream(content));
}
catch (Exception e)
{
throw new LoadXmlException($"打开定义文件:{xmlFile} 失败 --> {e.Message}");
}
}
public static string GetRequiredAttribute(XElement ele, string key)
{
if (ele.Attribute(key) != null)
{
var value = ele.Attribute(key).Value.Trim();
if (value.Length != 0)
{
return value;
}
}
throw new ArgumentException($"ele:{ele} key {key} 为空或未定义");
}
public static string GetOptionalAttribute(XElement ele, string key)
{
return ele.Attribute(key)?.Value ?? "";
}
public static bool GetOptionBoolAttribute(XElement ele, string key, bool defaultValue = false)
{
var attr = ele.Attribute(key)?.Value?.ToLower();
if (attr == null)
{
return defaultValue;
}
return attr == "1" || attr == "true";
}
public static int GetOptionIntAttribute(XElement ele, string key, int defaultValue = 0)
{
if (ele.Attribute(key) == null)
{
return defaultValue;
}
return int.Parse(ele.Attribute(key).Value);
}
public static int GetRequiredIntAttribute(XElement ele, string key)
{
var attr = ele.Attribute(key);
try
{
return int.Parse(attr.Value);
}
catch (Exception)
{
throw new FormatException($"{ele} 属性:{key}=>{attr?.Value} 不是整数");
}
}
public static XElement OpenRelate(string relatePath, string toOpenXmlFile)
{
return Open(FileUtil.Combine(relatePath, toOpenXmlFile));
}
}
}

View File

@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Source\TypeVisitors\Proto\**" />
<EmbeddedResource Remove="Source\TypeVisitors\Proto\**" />
<None Remove="Source\TypeVisitors\Proto\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ExcelDataReader" Version="3.6.0" />
<PackageReference Include="NeoLua" Version="1.3.11" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Luban.Job.Common\Luban.Job.Common.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,45 @@
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.Defs;
using System.Collections.Concurrent;
using System.Collections.Generic;
namespace Luban.Job.Cfg.Cache
{
/// <summary>
/// 配置加载记录缓存。
/// 如果某个表对应的数据文件未修改,定义没变化,那加载后的数据应该是一样的。
/// </summary>
class FileRecordCacheManager
{
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
public static FileRecordCacheManager Ins { get; } = new FileRecordCacheManager();
private readonly ConcurrentDictionary<(string, string, string, bool), (DefTable, List<DType>)> _caches = new ConcurrentDictionary<(string, string, string, bool), (DefTable, List<DType>)>();
public bool TryGetCacheLoadedRecords(DefTable table, string md5, string originFile, string sheetName, bool exportTestData, out List<DType> cacheRecords)
{
// TODO text localization check
cacheRecords = null;
if (!_caches.TryGetValue((table.Assembly.TimeZone.Id, md5, sheetName, exportTestData), out var r))
{
return false;
}
if (r.Item1.ValueTType.GetBeanAs<DefBean>().IsDefineEquals(table.ValueTType.GetBeanAs<DefBean>()))
{
cacheRecords = r.Item2;
s_logger.Trace("hit cache. table:{table} file:{file} md5:{md5}", table.FullName, originFile, md5);
return true;
}
else
{
return false;
}
}
public void AddCacheLoadedRecords(DefTable table, string md5, string sheetName, bool exportTestData, List<DType> cacheRecords)
{
_caches[(table.Assembly.TimeZone.Id, md5, sheetName, exportTestData)] = (table, cacheRecords);
}
}
}

View File

@ -0,0 +1,16 @@
using Luban.Job.Cfg.Datas;
using Luban.Job.Common.Types;
using System.Collections.Generic;
using System.IO;
namespace Luban.Job.Cfg.DataSources
{
abstract class AbstractDataSource
{
public abstract DType ReadOne(TBean type);
public abstract List<DType> ReadMulti(TBean type);
public abstract void Load(string rawUrl, string sheetName, Stream stream, bool exportDebugData);
}
}

View File

@ -0,0 +1,26 @@
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, bool exportDebugData)
{
throw new NotImplementedException();
}
public override List<DType> ReadMulti(TBean type)
{
throw new NotImplementedException();
}
public override DType ReadOne(TBean type)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.IO;
namespace Luban.Job.Cfg.DataSources
{
static class DataSourceFactory
{
public static AbstractDataSource Create(string url, string sheetName, Stream stream, bool exportTestData)
{
try
{
string ext = url.Contains('.') ? Path.GetExtension(url)?[1..] : url;
AbstractDataSource source;
switch (ext)
{
case "csv":
case "xls":
case "xlsx": source = new Excel.ExcelDataSource(); break;
case "xml": source = new Xml.XmlDataSource(); break;
case "lua": source = new Lua.LuaDataSource(); break;
case "json": source = new Json.JsonDataSource(); break;
case "b": source = new Binary.BinaryDataSource(); break;
default: throw new Exception($"不支持的文件类型:{url}");
}
source.Load(url, sheetName, stream, exportTestData);
return source;
}
catch (Exception e)
{
throw new Exception($"文件{url} 加载失败 ==> {e.Message}", e);
}
}
}
}

View File

@ -0,0 +1,85 @@
using ExcelDataReader;
using Luban.Job.Cfg.Datas;
using Luban.Job.Common.Types;
using System;
using System.Collections.Generic;
using System.IO;
namespace Luban.Job.Cfg.DataSources.Excel
{
class ExcelDataSource : AbstractDataSource
{
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
private readonly List<Sheet> _sheets = new List<Sheet>();
public override void Load(string rawUrl, string sheetName, Stream stream, bool exportTestData)
{
s_logger.Trace("{filename} {sheet}", rawUrl, sheetName);
string ext = Path.GetExtension(rawUrl);
using (var reader = ext != ".csv" ? ExcelReaderFactory.CreateReader(stream) : ExcelReaderFactory.CreateCsvReader(stream))
{
do
{
if (sheetName == null || reader.Name == sheetName)
{
try
{
var sheet = ReadSheet(reader, exportTestData);
if (sheet != null)
{
_sheets.Add(sheet);
}
}
catch (Exception e)
{
throw new Exception($"excel:{rawUrl} sheet:{reader.Name} 读取失败. ==> {e.Message}", e);
}
}
} while (reader.NextResult());
}
if (_sheets.Count == 0)
{
throw new Exception($"excel:{rawUrl} 不包含有效的单元薄(有效单元薄的A0单元格必须是##).");
}
}
private Sheet ReadSheet(IExcelDataReader reader, bool exportTestData)
{
var sheet = new Sheet(reader.Name ?? "", exportTestData);
return sheet.Load(reader) ? sheet : null;
}
public override List<DType> ReadMulti(TBean type)
{
var datas = new List<DType>();
foreach (var sheet in _sheets)
{
try
{
datas.AddRange(sheet.ReadMulti(type));
}
catch (Exception e)
{
throw new Exception($"sheet:{sheet.Name} ==> {e.Message} {e.StackTrace}", e);
}
}
return datas;
}
public override DType ReadOne(TBean type)
{
var datas = ReadMulti(type);
switch (datas.Count)
{
case 1: return datas[0];
case 0: throw new Exception($"单例表不能为空必须包含且只包含1个记录");
default: throw new Exception($"单例表必须恰好包含1个记录. 但当前记录数为:{datas.Count}");
}
}
}
}

View File

@ -0,0 +1,182 @@
using Luban.Job.Cfg.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Luban.Job.Cfg.DataSources.Excel
{
class ExcelStream
{
private readonly List<Sheet.Cell> _datas;
private readonly int _toIndex;
private int _curIndex;
public ExcelStream(List<Sheet.Cell> datas, int fromIndex, int toIndex, string sep)
{
if (string.IsNullOrWhiteSpace(sep))
{
this._datas = datas;
this._toIndex = toIndex;
this._curIndex = fromIndex;
}
else
{
this._datas = new List<Sheet.Cell>();
for (int i = fromIndex; i <= toIndex; i++)
{
var cell = datas[i];
object d = cell.Value;
if (d is string s)
{
this._datas.AddRange(DataUtil.SplitStringByAnySepChar(s, sep).Select(x => new Sheet.Cell(cell.Row, cell.Column, x)));
}
else
{
this._datas.Add(cell);
}
}
this._curIndex = 0;
this._toIndex = this._datas.Count - 1;
}
}
public ExcelStream(Sheet.Cell cell, string sep)
{
if (string.IsNullOrWhiteSpace(sep))
{
this._datas = new List<Sheet.Cell> { cell };
this._toIndex = 0;
this._curIndex = 0;
}
else
{
this._datas = new List<Sheet.Cell>();
object d = cell.Value;
if (!IsSkip(d))
{
if (d is string s)
{
this._datas.AddRange(DataUtil.SplitStringByAnySepChar(s, sep).Where(x => !IsSkip(x)).Select(x => new Sheet.Cell(cell.Row, cell.Column, x)));
}
else
{
this._datas.Add(cell);
}
}
this._curIndex = 0;
this._toIndex = this._datas.Count - 1;
}
}
public string First => _datas[_curIndex].Value?.ToString();
public string CurrentExcelPosition => _datas[Math.Min(_curIndex, _datas.Count - 1)].ToString();
public override string ToString()
{
var sb = new StringBuilder();
sb.Append('[');
for (int i = _curIndex; i <= _toIndex; i++)
{
sb.Append(_datas[i].Value);
sb.Append(',');
}
sb.Append(']');
return sb.ToString();
}
public bool TryRead(out object data)
{
data = null;
while (_curIndex <= _toIndex)
{
data = _datas[_curIndex++].Value;
if (!IsSkip(data))
{
return true;
}
}
return false;
}
public object Read()
{
//if (curIndex <= toIndex)
//{
// return datas[curIndex++].Value;
//}
//else
//{
// throw new Exception($"cell:{datas[curIndex - 1]} 无法读取到足够多的数据");
//}
return ReadSkipNull();
}
//public object Read(bool nullable)
//{
// return nullable ? Read() : ReadSkipNull();
//}
public Sheet.Cell ReadCell()
{
while (_curIndex <= _toIndex)
{
var data = _datas[_curIndex++];
if (!IsSkip(data.Value))
{
return data;
}
}
throw new Exception($"cell:{_datas[_curIndex - 1]} 缺少数据");
}
public object ReadSkipNull()
{
while (_curIndex <= _toIndex)
{
var data = _datas[_curIndex++];
if (!IsSkip(data.Value))
{
return data.Value;
}
}
throw new Exception($"cell:{_datas[_curIndex - 1]} 缺少数据");
}
private const string END_OF_LIST = "}";
private bool IsSkip(object x)
{
return x == null || (x is string s && string.IsNullOrEmpty(s));
}
public bool TryReadEOF()
{
int oldIndex = _curIndex;
while (_curIndex <= _toIndex)
{
var value = _datas[_curIndex++].Value?.ToString();
if (!IsSkip(value))
{
if (value == END_OF_LIST)
{
return true;
}
else
{
_curIndex = oldIndex;
return false;
}
}
}
_curIndex = oldIndex;
return true;
}
}
}

View File

@ -0,0 +1,660 @@
using ExcelDataReader;
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.TypeVisitors;
using Luban.Job.Common.Types;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Luban.Job.Cfg.DataSources.Excel
{
class Sheet
{
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
private bool OrientRow { get; set; } = true; // 以行为数据读取方向
private bool Align { get; set; } = true;// 标题头与数据严格对齐的 固定列格式
private bool IsMultiRow { get; set; }
private int TitleRows { get; set; } = 3; // 默认有三行是标题行. 第一行是字段名,第二行是中文描述,第三行是注释
public string Name { get; }
private List<List<Cell>> _rowColumns;
private Title _rootTitle;
private bool ExportTestData { get; }
public class Title
{
public int FromIndex { get; set; }
public int ToIndex { get; set; }
public string Name { get; set; }
public Dictionary<string, Title> SubTitles { get; set; } = new Dictionary<string, Title>();
public List<Title> SubTitleList { get; set; } = new List<Title>();
public void AddSubTitle(Title title)
{
if (!SubTitles.TryAdd(title.Name, title))
{
throw new Exception($"标题:{title.Name} 重复");
}
SubTitleList.Add(title);
}
// 由于先处理merge再处理只占一列的标题头.
// sub titles 未必是有序的。对于大多数数据并无影响
// 但对于 list类型的多级标题头有可能导致element 数据次序乱了
public void SortSubTitles()
{
SubTitleList.Sort((t1, t2) => t1.FromIndex - t2.FromIndex);
foreach (var t in SubTitleList)
{
t.SortSubTitles();
}
}
public override string ToString()
{
return $"name:{Name} [{FromIndex}, {ToIndex}] sub titles:[{string.Join(",\\n", SubTitleList)}]";
}
}
public struct Cell
{
public Cell(int row, int column, object value)
{
this.Row = row;
this.Column = column;
this.Value = value;
}
public int Row { get; } // 从 1 开始
public int Column { get; } // 从 0 开始,考虑改了它?
public object Value { get; }
private static string ToAlphaString(int column)
{
int h = column / 26;
int n = column % 26;
return $"{(h > 0 ? ((char)('A' + h - 1)).ToString() : "")}{(char)('A' + n)}";
}
public override string ToString()
{
return $"[{ToAlphaString(Column)}:{Row + 1}] {Value}";
}
}
public class NamedRow
{
public Title SelfTitle { get; }
public List<List<Cell>> Rows { get; }
public Dictionary<string, Title> Titles => SelfTitle.SubTitles;
public List<Title> TitleList => SelfTitle.SubTitleList;
public NamedRow(Title selfTitle, List<Cell> row)
{
SelfTitle = selfTitle;
Rows = new List<List<Cell>>() { row };
}
public NamedRow(Title selfTitle, List<List<Cell>> rows)
{
SelfTitle = selfTitle;
Rows = rows;
}
public int RowCount => Rows.Count;
private void CheckEmptySinceSecondRow(string name, int fromIndex, int toIndex)
{
for (int i = 1; i < Rows.Count; i++)
{
var row = Rows[i];
if (!IsBlankRow(row, fromIndex, toIndex))
{
throw new Exception($"字段:{name} 不是多行字段,只能第一行填值. {Bright.Common.StringUtil.CollectionToString(row)}");
}
}
}
public Title GetTitle(string name)
{
return Titles.TryGetValue(name, out var title) ? title : null;
}
public ExcelStream GetColumn(string name, string sep)
{
if (Titles.TryGetValue(name, out var title))
{
CheckEmptySinceSecondRow(name, title.FromIndex, title.ToIndex);
var es = new ExcelStream(Rows[0], title.FromIndex, title.ToIndex, sep);
return es;
}
else
{
throw new Exception($"单元薄 缺失 列:{name},请检查是否写错或者遗漏");
}
}
public NamedRow GetSubTitleNamedRow(string name)
{
Title title = this.Titles[name];
CheckEmptySinceSecondRow(name, title.FromIndex, title.ToIndex);
return new NamedRow(title, this.Rows[0]);
}
public NamedRow GetSubTitleNamedRowOfMultiRows(string name)
{
Title title = Titles[name];
return new NamedRow(title, this.Rows);
}
public IEnumerable<NamedRow> GenerateSubNameRows()
{
foreach (var row in Rows)
{
if (SelfTitle != null ? IsBlankRow(row, SelfTitle.FromIndex, SelfTitle.ToIndex) : IsBlankRow(row))
{
continue;
}
yield return new NamedRow(SelfTitle, row);
}
}
public IEnumerable<ExcelStream> GetColumnOfMultiRows(string name, string sep)
{
if (Titles.TryGetValue(name, out var title))
{
foreach (var row in Rows)
{
if (IsBlankRow(row, title.FromIndex, title.ToIndex))
{
continue;
}
yield return new ExcelStream(row, title.FromIndex, title.ToIndex, sep);
}
}
else
{
throw new Exception($"单元薄 缺失 列:{name},请检查是否写错或者遗漏");
}
}
}
public Sheet(string name, bool exportTestData)
{
this.Name = name;
this.ExportTestData = exportTestData;
}
public bool Load(IExcelDataReader reader)
{
//s_logger.Info("read sheet:{sheet}", reader.Name);
if (!ParseMeta(reader))
{
return false;
}
s_logger.Trace("align:{align} row:{orient}", Align, OrientRow);
if (!Align)
{
throw new Exception($"当前不支持 align:false");
}
LoadRemainRows(reader);
return true;
}
private bool ParseMeta(IExcelDataReader reader)
{
if (!reader.Read() || reader.FieldCount == 0)
{
return false;
}
// meta 行 必须以 ##为第一个单元格内容,紧接着 key:value 形式 表达meta属性
if (reader.GetString(0) != "##")
{
return false;
}
for (int i = 1, n = reader.FieldCount; i < n; i++)
{
var attr = reader.GetString(i);
if (string.IsNullOrWhiteSpace(attr))
{
continue;
}
var ss = attr.Split(':');
if (ss.Length != 2)
{
throw new Exception($"单元薄 meta 定义出错. attribute:{attr}");
}
string key = ss[0].ToLower();
string value = ss[1];
switch (key)
{
case "align":
{
if (!bool.TryParse(value, out var v))
{
throw new Exception($"单元薄 meta 定义 align:{value} 属性值只能为true或false");
}
Align = v;
break;
}
case "row":
{
if (!bool.TryParse(value, out var v))
{
throw new Exception($"单元薄 meta 定义 row:{value} 属性值只能为true或false");
}
OrientRow = v;
break;
}
case "multi_rows":
{
if (!bool.TryParse(value, out var v))
{
throw new Exception($"单元薄 meta 定义 multi_rows:{value} 属性值只能为true或false");
}
IsMultiRow = v;
break;
}
case "title_rows":
{
if (!int.TryParse(value, out var v))
{
throw new Exception($"单元薄 meta 定义 title_rows:{value} 属性值只能为整数[1,10]");
}
if (v < 1 || v > 10)
{
throw new Exception($"单元薄 title_rows 应该在 [1,10] 范围内,默认是3");
}
TitleRows = v;
break;
}
case "ignore":
{
if (!bool.TryParse(value, out var v))
{
throw new Exception($"单元薄 meta 定义 ignore:{value} 属性值只能为true或false");
}
if (v)
{
return false;
}
break;
}
default:
{
throw new Exception($"非法单元薄 meta 属性定义 {attr}");
}
}
}
return true;
}
private bool NotExport(List<Cell> row)
{
if (row.Count == 0)
{
return true;
}
if (row[0].Value == null)
{
return false;
}
string exportFlag = row[0].Value.ToString().Trim().ToLower();
switch (exportFlag)
{
case "false":
case "否": return true;
case "true":
case "是": return false;
case "test":
case "测试":
{
if (!ExportTestData)
{
s_logger.Debug("忽略测试数据. row:{row}", row);
}
return !ExportTestData;
}
default: throw new Exception($"不支持的excel 导出标记: {exportFlag}");
}
}
private void InitSubTitles(Title parentTitle, List<List<Cell>> rows, CellRange[] mergeCells, int maxDepth, int depth, int fromColumn, int toColumn)
{
List<Cell> row = rows[depth];
//if (row.Count > fromColumn)
//{
// row = row.GetRange(fromColumn, Math.Min(row.Count, toColumn + 1) - fromColumn);
//}
foreach (var mergeCell in mergeCells)
{
if (mergeCell.FromRow == depth + 1 && mergeCell.FromColumn >= fromColumn && mergeCell.ToColumn <= toColumn)
{
string subTitleName = row[mergeCell.FromColumn].Value?.ToString().Trim();
if (!string.IsNullOrWhiteSpace(subTitleName))
{
var newTitle = new Title() { Name = subTitleName, FromIndex = mergeCell.FromColumn, ToIndex = mergeCell.ToColumn };
if (depth + 1 < maxDepth)
{
InitSubTitles(newTitle, rows, mergeCells, maxDepth, depth + 1, mergeCell.FromColumn, mergeCell.ToColumn);
}
parentTitle.AddSubTitle(newTitle);
}
}
}
for (int i = fromColumn; i <= toColumn; i++)
{
if (i >= row.Count)
{
break;
}
var name = row[i].Value?.ToString()?.Trim();
if (string.IsNullOrWhiteSpace(name))
{
continue;
}
if (parentTitle.SubTitles.TryGetValue(name, out var oldTitle))
{
if (oldTitle.FromIndex != i)
{
throw new Exception($"sub title 列:{name} 重复");
}
else
{
continue;
}
}
var newTitle = new Title() { Name = name, FromIndex = i, ToIndex = i };
if (depth + 1 < maxDepth)
{
InitSubTitles(newTitle, rows, mergeCells, maxDepth, depth + 1, i, i);
}
parentTitle.AddSubTitle(newTitle);
}
}
private void LoadRemainRows(IExcelDataReader reader)
{
// TODO 优化性能
// 几个思路
// 1. 没有 title 的列不加载
// 2. 空行优先跳过
// 3. 跳过null或者empty的单元格
var rows = new List<List<Cell>>();
int rowIndex = 0;
while (reader.Read())
{
++rowIndex; // 第一行是 meta ,跳过
var row = new List<Cell>();
for (int i = 0, n = reader.FieldCount; i < n; i++)
{
row.Add(new Cell(rowIndex, i, reader.GetValue(i)));
}
rows.Add(row);
}
if (OrientRow)
{
this._rowColumns = rows;
}
else
{
// 转置这个行列
int maxColumn = rows.Select(r => r.Count).Max();
this._rowColumns = new List<List<Cell>>();
for (int i = 0; i < maxColumn; i++)
{
var row = new List<Cell>();
for (int j = 0; j < rows.Count; j++)
{
row.Add(i < rows[i].Count ? rows[j][i] : new Cell(j + 1, i, null));
}
this._rowColumns.Add(row);
}
}
if (this._rowColumns.Count < 1)
{
throw new Exception($"没有定义字段名行");
}
_rootTitle = new Title() { Name = "_root_", FromIndex = 1, ToIndex = rows.Select(r => r.Count).Max() - 1 };
int titleRowNum = 1;
if (reader.MergeCells != null)
{
if (OrientRow)
{
foreach (var mergeCell in reader.MergeCells)
{
if (mergeCell.FromRow == 1 && mergeCell.FromColumn == 0 && mergeCell.ToColumn == 0)
{
titleRowNum = mergeCell.ToRow - mergeCell.FromRow + 1;
}
}
}
foreach (var mergeCell in reader.MergeCells)
{
if (OrientRow)
{
//if (mergeCell.FromRow <= 1 && mergeCell.ToRow >= 1)
if (mergeCell.FromRow == 1)
{
// 标题 行
titleRowNum = Math.Max(titleRowNum, mergeCell.ToRow - mergeCell.FromRow + 1);
var titleName = _rowColumns[0][mergeCell.FromColumn].Value?.ToString()?.Trim();
if (string.IsNullOrWhiteSpace(titleName))
{
continue;
}
var newTitle = new Title() { Name = titleName, FromIndex = mergeCell.FromColumn, ToIndex = mergeCell.ToColumn };
if (titleRowNum > 1)
{
InitSubTitles(newTitle, rows, reader.MergeCells, titleRowNum, 1, mergeCell.FromColumn, mergeCell.ToColumn);
}
_rootTitle.AddSubTitle(newTitle);
//s_logger.Info("=== sheet:{sheet} title:{title}", Name, newTitle);
}
}
else
{
if (mergeCell.FromColumn <= 0 && mergeCell.ToColumn >= 0)
{
// 标题 行
var titleName = _rowColumns[0][mergeCell.FromRow - 1].Value?.ToString()?.Trim();
if (string.IsNullOrWhiteSpace(titleName))
{
continue;
}
_rootTitle.AddSubTitle(new Title() { Name = titleName, FromIndex = mergeCell.FromRow - 1, ToIndex = mergeCell.ToRow - 1 });
}
}
}
}
//TODO 其实有bug. 未处理只占一列的 多级标题头
// 有一些列不是MergeCell,所以还需要额外处理
var titleRow = _rowColumns[0];
for (int i = 0; i < titleRow.Count; i++)
{
var name = titleRow[i].Value?.ToString()?.Trim();
if (string.IsNullOrWhiteSpace(name))
{
continue;
}
if (_rootTitle.SubTitles.TryGetValue(name, out var oldTitle))
{
if (oldTitle.FromIndex != i)
{
throw new Exception($"列:{name} 重复");
}
else
{
continue;
}
}
_rootTitle.AddSubTitle(new Title() { Name = name, FromIndex = i, ToIndex = i });
}
if (_rootTitle.SubTitleList.Count == 0)
{
throw new Exception($"没有定义任何有效 列");
}
_rootTitle.SortSubTitles();
foreach (var title in _rootTitle.SubTitleList)
{
// s_logger.Info("============ sheet:{sheet} title:{title}", Name, title);
}
// 删除标题行
this._rowColumns.RemoveRange(0, Math.Min(TitleRows + titleRowNum - 1, this._rowColumns.Count));
this._rowColumns.RemoveAll(row => NotExport(row));
}
public static bool IsBlankRow(List<Cell> row)
{
// 第一列被策划用于表示是否注释掉此行
// 忽略此列是否空白
return row.GetRange(1, row.Count - 1).All(c => c.Value == null || (c.Value is string s && string.IsNullOrWhiteSpace(s)));
}
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++)
{
var v = row[i].Value;
if (v != null && !(v is string s && string.IsNullOrEmpty(s)))
{
return false;
}
}
return true;
}
private List<Cell> GetNextRecordRow()
{
while (curReadIndex < _rowColumns.Count)
{
var row = _rowColumns[curReadIndex++];
if (IsBlankRow(row))
{
continue;
}
return row;
}
return null;
}
private bool HasNotMainKey(List<Cell> row)
{
return string.IsNullOrWhiteSpace(row[1].Value?.ToString());
}
private List<List<Cell>> GetNextRecordRows()
{
List<List<Cell>> rows = null;
while (curReadIndex < _rowColumns.Count)
{
var row = _rowColumns[curReadIndex++];
if (IsBlankRow(row))
{
continue;
}
if (rows == null)
{
rows = new List<List<Cell>>() { row };
}
else
{
if (HasNotMainKey(row))
{
rows.Add(row);
}
else
{
--curReadIndex;
return rows;
}
}
}
return rows;
}
public List<DType> ReadMulti(TBean type)
{
var datas = new List<DType>();
for (DType data; (data = ReadOne(type)) != null;)
{
datas.Add(data);
}
return datas;
}
private int curReadIndex = 0;
public DType ReadOne(TBean type)
{
if (!IsMultiRow)
{
List<Cell> row = GetNextRecordRow();
if (row == null)
{
return null;
}
return ExcelNamedRowDataCreator.Ins.ReadExcel(new NamedRow(_rootTitle, row), type);
}
else
{
List<List<Cell>> rows = GetNextRecordRows();
if (rows == null)
{
return null;
}
return ExcelNamedRowDataCreator.Ins.ReadExcel(new NamedRow(_rootTitle, rows), type);
}
}
}
}

View File

@ -0,0 +1,31 @@
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.Defs;
using Luban.Job.Cfg.TypeVisitors;
using Luban.Job.Common.Types;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
namespace Luban.Job.Cfg.DataSources.Json
{
class JsonDataSource : AbstractDataSource
{
JsonElement _data;
public override void Load(string rawUrl, string sheetName, Stream stream, bool exportDebugData)
{
this._data = JsonDocument.Parse(stream).RootElement;
}
public override List<DType> ReadMulti(TBean type)
{
throw new NotImplementedException();
}
public override DType ReadOne(TBean type)
{
return type.Apply(JsonDataCreator.Ins, _data, (DefAssembly)type.Bean.AssemblyBase);
}
}
}

View File

@ -0,0 +1,42 @@
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.Defs;
using Luban.Job.Cfg.TypeVisitors;
using Luban.Job.Common.Types;
using Neo.IronLua;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Luban.Job.Cfg.DataSources.Lua
{
class LuaDataSource : AbstractDataSource
{
private static Neo.IronLua.Lua LuaManager { get; } = new Neo.IronLua.Lua();
private LuaGlobal _env;
private LuaTable _dataTable;
public override void Load(string rawUrl, string sheetName, Stream stream, bool exportDebugData)
{
_env = LuaManager.CreateEnvironment();
_dataTable = (LuaTable)_env.DoChunk(new StreamReader(stream, Encoding.UTF8), rawUrl)[0];
}
public override List<DType> ReadMulti(TBean type)
{
var records = new List<DType>();
foreach (LuaTable t in _dataTable.Values.Values)
{
records.Add(type.Apply(LuaDataCreator.Ins, t, (DefAssembly)type.Bean.AssemblyBase));
}
return records;
}
public override DType ReadOne(TBean type)
{
return type.Apply(LuaDataCreator.Ins, _dataTable, (DefAssembly)type.Bean.AssemblyBase);
}
}
}

View File

@ -0,0 +1,30 @@
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.Defs;
using Luban.Job.Cfg.TypeVisitors;
using Luban.Job.Common.Types;
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Linq;
namespace Luban.Job.Cfg.DataSources.Xml
{
class XmlDataSource : AbstractDataSource
{
private XElement _doc;
public override void Load(string rawUrl, string sheetName, Stream stream, bool exportDebugData)
{
_doc = XElement.Load(stream);
}
public override List<DType> ReadMulti(TBean type)
{
throw new NotSupportedException();
}
public override DType ReadOne(TBean type)
{
return type.Apply(XmlDataCreator.Ins, _doc, (DefAssembly)type.Bean.AssemblyBase);
}
}
}

View File

@ -0,0 +1,179 @@
using Bright.Serialization;
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.Defs;
using System.Collections.Generic;
namespace Luban.Job.Cfg.DataVisitors
{
class BinaryExportor : IDataActionVisitor<ByteBuf>
{
public static BinaryExportor Ins { get; } = new BinaryExportor();
public void Accept(DBool type, ByteBuf x)
{
x.WriteBool(type.Value);
}
public void Accept(DByte type, ByteBuf x)
{
x.WriteByte(type.Value);
}
public void Accept(DShort type, ByteBuf x)
{
x.WriteShort(type.Value);
}
public void Accept(DFshort type, ByteBuf x)
{
x.WriteFshort(type.Value);
}
public void Accept(DInt type, ByteBuf x)
{
x.WriteInt(type.Value);
}
public void Accept(DFint type, ByteBuf x)
{
x.WriteFint(type.Value);
}
public void Accept(DLong type, ByteBuf x)
{
x.WriteLong(type.Value);
}
public void Accept(DFlong type, ByteBuf x)
{
x.WriteFlong(type.Value);
}
public void Accept(DFloat type, ByteBuf x)
{
x.WriteFloat(type.Value);
}
public void Accept(DDouble type, ByteBuf x)
{
x.WriteDouble(type.Value);
}
public void Accept(DEnum type, ByteBuf x)
{
x.WriteInt(type.Value);
}
public void Accept(DString type, ByteBuf x)
{
x.WriteString(type.Value);
}
public void Accept(DBytes type, ByteBuf x)
{
x.WriteBytes(type.Value);
}
public void Accept(DText type, ByteBuf x)
{
x.WriteString(type.Value);
}
public void Accept(DBean type, ByteBuf x)
{
var bean = type.Type;
if (bean.IsAbstractType)
{
// null 时特殊处理
if (type.ImplType == null)
{
x.WriteInt(0);
return;
}
x.WriteInt(type.ImplType.Id);
}
int index = -1;
foreach (var field in type.Fields)
{
++index;
var defField = (DefField)type.ImplType.HierarchyFields[index];
if (!defField.NeedExport)
{
continue;
}
if (defField.NeedMarshalBoolPrefix)
{
if (field != null)
{
x.WriteBool(true);
field.Apply(this, x);
}
else
{
x.WriteBool(false);
}
}
else
{
field.Apply(this, x);
}
}
}
public void WriteList(List<DType> datas, ByteBuf x)
{
x.WriteSize(datas.Count);
foreach (var d in datas)
{
d.Apply(this, x);
}
}
public void Accept(DArray type, ByteBuf x)
{
WriteList(type.Datas, x);
}
public void Accept(DList type, ByteBuf x)
{
WriteList(type.Datas, x);
}
public void Accept(DSet type, ByteBuf x)
{
WriteList(type.Datas, x);
}
public void Accept(DMap type, ByteBuf x)
{
Dictionary<DType, DType> datas = type.Datas;
x.WriteSize(datas.Count);
foreach (var e in datas)
{
e.Key.Apply(this, x);
e.Value.Apply(this, x);
}
}
public void Accept(DVector2 type, ByteBuf x)
{
x.WriteVector2(type.Value);
}
public void Accept(DVector3 type, ByteBuf x)
{
x.WriteVector3(type.Value);
}
public void Accept(DVector4 type, ByteBuf x)
{
x.WriteVector4(type.Value);
}
public void Accept(DDateTime type, ByteBuf x)
{
x.WriteInt(type.UnixTime);
}
}
}

View File

@ -0,0 +1,102 @@
using Luban.Job.Cfg.Datas;
namespace Luban.Job.Cfg.DataVisitors
{
public interface IDataActionVisitor<T>
{
void Accept(DBool type, T x);
void Accept(DByte type, T x);
void Accept(DShort type, T x);
void Accept(DFshort type, T x);
void Accept(DInt type, T x);
void Accept(DFint type, T x);
void Accept(DLong type, T x);
void Accept(DFlong type, T x);
void Accept(DFloat type, T x);
void Accept(DDouble type, T x);
void Accept(DEnum type, T x);
void Accept(DString type, T x);
void Accept(DBytes type, T x);
void Accept(DText type, T x);
void Accept(DBean type, T x);
void Accept(DArray type, T x);
void Accept(DList type, T x);
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>
{
void Accept(DBool type, T1 x, T2 y);
void Accept(DByte type, T1 x, T2 y);
void Accept(DShort type, T1 x, T2 y);
void Accept(DFshort type, T1 x, T2 y);
void Accept(DInt type, T1 x, T2 y);
void Accept(DFint type, T1 x, T2 y);
void Accept(DLong type, T1 x, T2 y);
void Accept(DFlong type, T1 x, T2 y);
void Accept(DFloat type, T1 x, T2 y);
void Accept(DDouble type, T1 x, T2 y);
void Accept(DEnum type, T1 x, T2 y);
void Accept(DString type, T1 x, T2 y);
void Accept(DBytes type, T1 x, T2 y);
void Accept(DText type, T1 x, T2 y);
void Accept(DBean type, T1 x, T2 y);
void Accept(DArray type, T1 x, T2 y);
void Accept(DList type, T1 x, T2 y);
void Accept(DSet type, T1 x, T2 y);
void Accept(DMap type, T1 x, T2 y);
void Accept(DVector2 type, T1 x, T2 y);
void Accept(DVector3 type, T1 x, T2 y);
void Accept(DVector4 type, T1 x, T2 y);
void Accept(DDateTime type, T1 x, T2 y);
}
}

View File

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

View File

@ -0,0 +1,126 @@
using Luban.Job.Cfg.Datas;
using System;
using System.Numerics;
namespace Luban.Job.Cfg.DataVisitors
{
class IsDefaultValue : IDataFuncVisitor<bool>
{
public static IsDefaultValue Ins { get; } = new IsDefaultValue();
public bool Accept(DBool type)
{
return type.Value == false;
}
public bool Accept(DByte type)
{
return type.Value == 0;
}
public bool Accept(DShort type)
{
return type.Value == 0;
}
public bool Accept(DFshort type)
{
return type.Value == 0;
}
public bool Accept(DInt type)
{
return type.Value == 0;
}
public bool Accept(DFint type)
{
return type.Value == 0;
}
public bool Accept(DLong type)
{
return type.Value == 0;
}
public bool Accept(DFlong type)
{
return type.Value == 0;
}
public bool Accept(DFloat type)
{
return type.Value == 0;
}
public bool Accept(DDouble type)
{
return type.Value == 0;
}
public bool Accept(DEnum type)
{
return type.Value == 0;
}
public bool Accept(DString type)
{
return string.IsNullOrEmpty(type.Value);
}
public bool Accept(DBytes type)
{
throw new NotImplementedException();
}
public bool Accept(DText type)
{
return string.IsNullOrEmpty(type.Value);
}
public bool Accept(DBean type)
{
return false;
}
public bool Accept(DArray type)
{
return type.Datas.Count == 0;
}
public bool Accept(DList type)
{
return type.Datas.Count == 0;
}
public bool Accept(DSet type)
{
return type.Datas.Count == 0;
}
public bool Accept(DMap type)
{
return type.Datas.Count == 0;
}
public bool Accept(DVector2 type)
{
return type.Value == Vector2.Zero;
}
public bool Accept(DVector3 type)
{
return type.Value == Vector3.Zero;
}
public bool Accept(DVector4 type)
{
return type.Value == Vector4.Zero;
}
public bool Accept(DDateTime type)
{
return type.UnixTime == 0;
}
}
}

View File

@ -0,0 +1,187 @@
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.Defs;
using System;
using System.Collections.Generic;
using System.Text.Json;
namespace Luban.Job.Cfg.DataVisitors
{
class JsonExportor : IDataActionVisitor<Utf8JsonWriter>
{
public static JsonExportor Ins { get; } = new JsonExportor();
public void Accept(DBool type, Utf8JsonWriter x)
{
x.WriteBooleanValue(type.Value);
}
public void Accept(DByte type, Utf8JsonWriter x)
{
x.WriteNumberValue(type.Value);
}
public void Accept(DShort type, Utf8JsonWriter x)
{
x.WriteNumberValue(type.Value);
}
public void Accept(DFshort type, Utf8JsonWriter x)
{
x.WriteNumberValue(type.Value);
}
public void Accept(DInt type, Utf8JsonWriter x)
{
x.WriteNumberValue(type.Value);
}
public void Accept(DFint type, Utf8JsonWriter x)
{
x.WriteNumberValue(type.Value);
}
public void Accept(DLong type, Utf8JsonWriter x)
{
x.WriteNumberValue(type.Value);
}
public void Accept(DFlong type, Utf8JsonWriter x)
{
x.WriteNumberValue(type.Value);
}
public void Accept(DFloat type, Utf8JsonWriter x)
{
x.WriteNumberValue(type.Value);
}
public void Accept(DDouble type, Utf8JsonWriter x)
{
x.WriteNumberValue(type.Value);
}
public void Accept(DEnum type, Utf8JsonWriter x)
{
x.WriteNumberValue(type.Value);
}
public void Accept(DString type, Utf8JsonWriter x)
{
x.WriteStringValue(type.Value);
}
public void Accept(DBytes type, Utf8JsonWriter x)
{
throw new NotImplementedException();
}
public void Accept(DText type, Utf8JsonWriter x)
{
x.WriteStringValue(type.Value);
}
public void Accept(DBean type, Utf8JsonWriter x)
{
x.WriteStartObject();
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++];
if (!defField.NeedExport)
{
continue;
}
x.WritePropertyName(defField.Name);
// 特殊处理 bean 多态类型
if (d == null || (d is DBean db && db.ImplType == null))
{
x.WriteNullValue();
}
else
{
d.Apply(this, x);
}
}
x.WriteEndObject();
}
public void WriteList(List<DType> datas, Utf8JsonWriter x)
{
x.WriteStartArray();
foreach (var d in datas)
{
d.Apply(this, x);
}
x.WriteEndArray();
}
public void Accept(DArray type, Utf8JsonWriter x)
{
WriteList(type.Datas, x);
}
public void Accept(DList type, Utf8JsonWriter x)
{
WriteList(type.Datas, x);
}
public void Accept(DSet type, Utf8JsonWriter x)
{
WriteList(type.Datas, x);
}
public void Accept(DMap type, Utf8JsonWriter 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, Utf8JsonWriter x)
{
x.WriteStartObject();
x.WriteNumber("x", type.Value.X);
x.WriteNumber("y", type.Value.Y);
x.WriteEndObject();
}
public void Accept(DVector3 type, Utf8JsonWriter x)
{
x.WriteStartObject();
x.WriteNumber("x", type.Value.X);
x.WriteNumber("y", type.Value.Y);
x.WriteNumber("z", type.Value.Z);
x.WriteEndObject();
}
public void Accept(DVector4 type, Utf8JsonWriter x)
{
x.WriteStartObject();
x.WriteNumber("x", type.Value.X);
x.WriteNumber("y", type.Value.Y);
x.WriteNumber("z", type.Value.Z);
x.WriteNumber("w", type.Value.W);
x.WriteEndObject();
}
public void Accept(DDateTime type, Utf8JsonWriter x)
{
x.WriteNumberValue(type.UnixTime);
}
}
}

View File

@ -0,0 +1,246 @@
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.Defs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Luban.Job.Cfg.DataVisitors
{
class LuaExportor : IDataActionVisitor<StringBuilder>
{
public static LuaExportor Ins { get; } = new LuaExportor();
public void ExportTableOne(DefTable t, List<DType> records, List<string> result)
{
result.Add("return ");
var s = new StringBuilder();
Accept((DBean)records[0], s);
result.Add(s.ToString());
}
public void ExportTableOneKeyMap(DefTable t, List<DType> records, List<string> result)
{
result.Add("return ");
result.Add("{");
var s = new StringBuilder();
var ks = new StringBuilder();
foreach (DBean r in records)
{
s.Clear();
s.Append($"[{ToLuaCodeString(r.GetField(t.Index), ks)}] = ");
Accept(r, s);
s.Append(',');
result.Add(s.ToString());
}
result.Add("}");
}
public void ExportTableTwoKeyMap(DefTable t, List<DType> records, List<string> result)
{
result.Add("return ");
result.Add("{");
var s = new StringBuilder();
var ks = new StringBuilder();
foreach (var g in records.GroupBy(r => ((DBean)r).GetField(t.Index1)))
{
result.Add($"[{ToLuaCodeString(g.Key, ks)}] =");
result.Add("{");
foreach (DBean r in g)
{
s.Clear();
s.Append($"[{ToLuaCodeString(r.GetField(t.Index2), ks)}] = ");
Accept(r, s);
s.Append(',');
result.Add(s.ToString());
}
result.Add("},");
}
result.Add("}");
}
private string ToLuaCodeString(DType data, StringBuilder b)
{
b.Clear();
data.Apply(this, b);
return b.ToString();
}
public void Accept(DBool type, StringBuilder line)
{
line.Append(type.Value ? "true" : "false");
}
public void Accept(DByte type, StringBuilder line)
{
line.Append(type.Value);
}
public void Accept(DShort type, StringBuilder line)
{
line.Append(type.Value);
}
public void Accept(DFshort type, StringBuilder line)
{
line.Append(type.Value);
}
public void Accept(DInt type, StringBuilder line)
{
line.Append(type.Value);
}
public void Accept(DFint type, StringBuilder line)
{
line.Append(type.Value);
}
public void Accept(DLong type, StringBuilder line)
{
line.Append(type.Value);
}
public void Accept(DFlong type, StringBuilder line)
{
line.Append(type.Value);
}
public void Accept(DFloat type, StringBuilder line)
{
line.Append(type.Value);
}
public void Accept(DDouble type, StringBuilder line)
{
line.Append(type.Value);
}
public void Accept(DEnum type, StringBuilder line)
{
line.Append(type.Value);
}
public void Accept(DString type, StringBuilder line)
{
line.Append('\'').Append(type.Value).Append('\'');
}
public void Accept(DBytes type, StringBuilder line)
{
throw new NotImplementedException();
}
public void Accept(DText type, StringBuilder line)
{
line.Append('\'').Append(type.Value).Append('\'');
}
public void Accept(DBean type, StringBuilder line)
{
var bean = type.Type;
if (bean.IsAbstractType)
{
// null 时特殊处理
if (type.ImplType == null)
{
line.Append("nil");
return;
}
line.Append($"{{ _name='{type.ImplType.FullName}',");
}
else
{
line.Append('{');
}
int index = -1;
foreach (var field in type.Fields)
{
++index;
var defField = (DefField)type.ImplType.HierarchyFields[index];
if (!defField.NeedExport)
{
continue;
}
if (field != null)
{
line.Append(defField.Name).Append('=');
field.Apply(this, line);
line.Append(',');
}
}
line.Append("}");
}
public void Accept(DArray type, StringBuilder line)
{
line.Append('{');
foreach (var d in type.Datas)
{
d.Apply(this, line);
line.Append(',');
}
line.Append('}');
}
public void Accept(DList type, StringBuilder line)
{
line.Append('{');
foreach (var d in type.Datas)
{
d.Apply(this, line);
line.Append(',');
}
line.Append('}');
}
public void Accept(DSet type, StringBuilder line)
{
line.Append('{');
foreach (var d in type.Datas)
{
d.Apply(this, line);
line.Append(',');
}
line.Append('}');
}
public void Accept(DMap type, StringBuilder line)
{
line.Append('{');
foreach ((var k, var v) in type.Datas)
{
line.Append('[');
k.Apply(this, line);
line.Append("]=");
v.Apply(this, line);
line.Append(',');
}
line.Append('}');
}
public void Accept(DVector2 type, StringBuilder line)
{
line.Append($"{{x={type.Value.X},y={type.Value.Y}}}");
}
public void Accept(DVector3 type, StringBuilder line)
{
line.Append($"{{x={type.Value.X},y={type.Value.Y},z={type.Value.Z}}}");
}
public void Accept(DVector4 type, StringBuilder line)
{
line.Append($"{{x={type.Value.X},y={type.Value.Y},z={type.Value.Z},w={type.Value.W}}}");
}
public void Accept(DDateTime type, StringBuilder line)
{
line.Append(type.UnixTime);
}
}
}

View File

@ -0,0 +1,162 @@
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.Defs;
using Luban.Job.Cfg.RawDefs;
using Luban.Job.Common.Types;
using System;
using System.Collections.Generic;
namespace Luban.Job.Cfg.DataVisitors
{
class ResourceExportor : IDataActionVisitor<DefField, List<ResourceInfo>>
{
public static ResourceExportor Ins { get; } = new ResourceExportor();
public void Accept(DBool type, DefField x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DByte type, DefField x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DShort type, DefField x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DFshort type, DefField x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DInt type, DefField x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DFint type, DefField x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DLong type, DefField x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DFlong type, DefField x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DFloat type, DefField x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DDouble type, DefField x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DEnum type, DefField x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DString type, DefField x, List<ResourceInfo> y)
{
if (!string.IsNullOrEmpty(type.Value))
{
y.Add(new ResourceInfo() { Resource = type.Value, Tag = x.ResourceTag });
}
}
public void Accept(DBytes type, DefField x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DText type, DefField x, List<ResourceInfo> y)
{
throw new NotImplementedException();
}
public void Accept(DBean type, DefField _, 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);
}
}
}
private void Accept(DefField def, List<DType> datas, TType elementType, List<ResourceInfo> ress)
{
if (def.IsResource || (elementType is TBean))
{
foreach (var e in datas)
{
e.Apply(this, def, ress);
}
}
}
public void Accept(DArray type, DefField x, List<ResourceInfo> y)
{
Accept(x, type.Datas, type.Type.ElementType, y);
}
public void Accept(DList type, DefField x, List<ResourceInfo> y)
{
Accept(x, type.Datas, type.Type.ElementType, y);
}
public void Accept(DSet type, DefField x, List<ResourceInfo> y)
{
Accept(x, type.Datas, type.Type.ElementType, y);
}
public void Accept(DMap type, DefField x, List<ResourceInfo> y)
{
if (x.IsResource || (type.Type.ValueType is TBean))
{
foreach (var e in type.Datas.Values)
{
e.Apply(this, x, y);
}
}
}
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();
}
}
}

View File

@ -0,0 +1,170 @@
using Luban.Job.Cfg.Datas;
using System.Collections.Generic;
using System.Text;
namespace Luban.Job.Cfg.DataVisitors
{
class ToStringVisitor : IDataActionVisitor<StringBuilder>
{
public static ToStringVisitor Ins { get; } = new ToStringVisitor();
public void Accept(DBool type, StringBuilder x)
{
x.Append(type.Value);
}
public void Accept(DByte type, StringBuilder x)
{
x.Append(type.Value);
}
public void Accept(DShort type, StringBuilder x)
{
x.Append(type.Value);
}
public void Accept(DFshort type, StringBuilder x)
{
x.Append(type.Value);
}
public void Accept(DInt type, StringBuilder x)
{
x.Append(type.Value);
}
public void Accept(DFint type, StringBuilder x)
{
x.Append(type.Value);
}
public void Accept(DLong type, StringBuilder x)
{
x.Append(type.Value);
}
public void Accept(DFlong type, StringBuilder x)
{
x.Append(type.Value);
}
public void Accept(DFloat type, StringBuilder x)
{
x.Append(type.Value);
}
public void Accept(DDouble type, StringBuilder x)
{
x.Append(type.Value);
}
public void Accept(DEnum type, StringBuilder x)
{
x.Append(type.StrValue);
}
public void Accept(DString type, StringBuilder x)
{
x.Append(type.Value);
}
public void Accept(DBytes type, StringBuilder x)
{
x.Append(Bright.Common.StringUtil.ArrayToString(type.Value));
}
public void Accept(DText type, StringBuilder x)
{
x.Append(type.Value);
}
public void Accept(DBean type, StringBuilder x)
{
if (type.ImplType == null)
{
x.Append("null");
return;
}
x.Append(type.ImplType.FullName).Append('{');
int index = 0;
foreach (var f in type.Fields)
{
var defField = type.ImplType.HierarchyFields[index++];
x.Append(defField.Name).Append(':');
if (f != null)
{
f.Apply(this, x);
}
else
{
x.Append("null");
}
x.Append(',');
}
x.Append('}');
}
private void Append(List<DType> datas, StringBuilder x)
{
x.Append('{');
foreach (var e in datas)
{
e.Apply(this, x);
x.Append(',');
}
x.Append('}');
}
public void Accept(DArray type, StringBuilder x)
{
Append(type.Datas, x);
}
public void Accept(DList type, StringBuilder x)
{
Append(type.Datas, x);
}
public void Accept(DSet type, StringBuilder x)
{
Append(type.Datas, x);
}
public void Accept(DMap type, StringBuilder x)
{
x.Append('{');
foreach (var e in type.Datas)
{
e.Key.Apply(this, x);
x.Append(':');
e.Value.Apply(this, x);
x.Append(',');
}
x.Append('}');
}
public void Accept(DVector2 type, StringBuilder x)
{
var v = type.Value;
x.Append($"vector2(x={v.X},y={v.Y})");
}
public void Accept(DVector3 type, StringBuilder x)
{
var v = type.Value;
x.Append($"vector3(x={v.X},y={v.Y},z={v.Z})");
}
public void Accept(DVector4 type, StringBuilder x)
{
var v = type.Value;
x.Append($"vector4(x={v.X},y={v.Y},z={v.Z},w={v.W})");
}
public void Accept(DDateTime type, StringBuilder x)
{
x.Append(type.Time.ToString());
}
}
}

View File

@ -0,0 +1,327 @@
using Luban.Common.Utils;
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.Defs;
using Luban.Job.Common.Types;
using System;
using System.Collections.Generic;
namespace Luban.Job.Cfg.DataVisitors
{
public class ValidatorVisitor : IDataActionVisitor<DefAssembly>
{
private readonly Stack<object> _path = new Stack<object>();
public Stack<object> Path => _path;
public ValidatorContext Ctx { get; }
public DBean CurrentValidateRecord { get; private set; }
public ValidatorVisitor(ValidatorContext ctx)
{
Ctx = ctx;
}
public void ValidateTable(DefTable table, List<DType> records)
{
DefAssembly ass = table.Assembly;
var keyIndex = table.IndexFieldIdIndex;
foreach (DBean r in records)
{
CurrentValidateRecord = r;
_path.Clear();
_path.Push(table.FullName);
if (table.IsMapTable)
{
_path.Push(r.Fields[keyIndex]);
}
else if (table.IsTwoKeyMapTable)
{
_path.Push(r.Fields[keyIndex]);
_path.Push(r.Fields[table.IndexFieldIdIndex2]);
}
Accept(r, ass);
}
}
public void Accept(DBool type, DefAssembly x)
{
throw new NotImplementedException();
}
public void Accept(DByte type, DefAssembly x)
{
throw new NotImplementedException();
}
public void Accept(DShort type, DefAssembly x)
{
throw new NotImplementedException();
}
public void Accept(DFshort type, DefAssembly x)
{
throw new NotImplementedException();
}
public void Accept(DInt type, DefAssembly x)
{
throw new NotImplementedException();
}
public void Accept(DFint type, DefAssembly x)
{
throw new NotImplementedException();
}
public void Accept(DLong type, DefAssembly x)
{
throw new NotImplementedException();
}
public void Accept(DFlong type, DefAssembly x)
{
throw new NotImplementedException();
}
public void Accept(DFloat type, DefAssembly x)
{
throw new NotImplementedException();
}
public void Accept(DDouble type, DefAssembly x)
{
throw new NotImplementedException();
}
public void Accept(DEnum type, DefAssembly x)
{
throw new NotImplementedException();
}
public void Accept(DString type, DefAssembly x)
{
throw new NotImplementedException();
}
public void Accept(DBytes type, DefAssembly x)
{
throw new NotImplementedException();
}
public void Accept(DText type, DefAssembly x)
{
throw new NotImplementedException();
}
public void Accept(DBean record, DefAssembly assembly)
{
if (record.ImplType == null)
{
return;
}
var defFields = record.ImplType.HierarchyFields;
int i = 0;
foreach (var fieldValue in record.Fields)
{
var defField = (DefField)defFields[i++];
_path.Push(defField.Name);
switch (defField.CType)
{
case TArray a:
{
if (defField.ValueValidators.Count > 0)
{
var arr = (DArray)fieldValue;
int index = 0;
foreach (var value in arr.Datas)
{
_path.Push(index++);
foreach (var v in defField.ValueValidators)
{
v.Validate(Ctx, value, defField.IsNullable);
}
_path.Pop();
}
}
if (a.ElementType is TBean)
{
var arr = (DArray)fieldValue;
int index = 0;
foreach (var value in arr.Datas)
{
_path.Push(index++);
Accept((DBean)value, assembly);
_path.Pop();
}
}
break;
}
case TList b:
{
if (defField.ValueValidators.Count > 0)
{
var arr = (DList)fieldValue;
int index = 0;
foreach (var value in arr.Datas)
{
_path.Push(index++);
foreach (var v in defField.ValueValidators)
{
v.Validate(Ctx, value, false);
}
_path.Pop();
}
}
if (b.ElementType is TBean tb)
{
var arr = (DList)fieldValue;
int index = 0;
foreach (var value in arr.Datas)
{
_path.Push(index++);
Accept((DBean)value, assembly);
_path.Pop();
}
if (defField.IndexField != null)
{
var indexSet = new HashSet<DType>();
if (!tb.GetBeanAs<DefBean>().TryGetField(defField.Index, out var _, out var indexOfIndexField))
{
throw new Exception("impossible");
}
foreach (var value in arr.Datas)
{
_path.Push(index++);
DType indexValue = ((DBean)value).Fields[indexOfIndexField];
if (!indexSet.Add(indexValue))
{
throw new Exception($"{TypeUtil.MakeFullName(_path)} index:{indexValue} 重复");
}
_path.Pop();
}
}
}
break;
}
case TSet c:
{
if (defField.ValueValidators.Count > 0)
{
var arr = (DSet)fieldValue;
foreach (var value in arr.Datas)
{
foreach (var v in defField.ValueValidators)
{
v.Validate(Ctx, value, false);
}
}
}
break;
}
case TMap m:
{
DMap map = (DMap)fieldValue;
if (defField.KeyValidators.Count > 0)
{
foreach (var key in map.Datas.Keys)
{
_path.Push(key);
foreach (var v in defField.KeyValidators)
{
v.Validate(Ctx, key, false);
}
_path.Pop();
}
}
if (defField.ValueValidators.Count > 0)
{
foreach (var value in map.Datas.Values)
{
_path.Push(value);
foreach (var v in defField.ValueValidators)
{
v.Validate(Ctx, value, false);
}
if (value is DBean dv)
{
Accept(dv, assembly);
}
_path.Pop();
}
}
break;
}
case TBean n:
{
Accept((DBean)fieldValue, assembly);
break;
}
default:
{
if (defField.Validators.Count > 0)
{
foreach (var v in defField.Validators)
{
v.Validate(Ctx, fieldValue, defField.IsNullable);
}
}
break;
}
}
_path.Pop();
}
}
public void Accept(DArray type, DefAssembly x)
{
throw new NotImplementedException();
}
public void Accept(DList type, DefAssembly x)
{
throw new NotImplementedException();
}
public void Accept(DSet type, DefAssembly x)
{
throw new NotImplementedException();
}
public void Accept(DMap type, DefAssembly x)
{
throw new NotImplementedException();
}
public void Accept(DVector2 type, DefAssembly x)
{
throw new NotImplementedException();
}
public void Accept(DVector3 type, DefAssembly x)
{
throw new NotImplementedException();
}
public void Accept(DVector4 type, DefAssembly x)
{
throw new NotImplementedException();
}
public void Accept(DDateTime type, DefAssembly x)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,38 @@
using Luban.Job.Cfg.DataVisitors;
using Luban.Job.Common.Types;
using System.Collections.Generic;
namespace Luban.Job.Cfg.Datas
{
public class DArray : DType
{
public TArray Type { get; }
public List<DType> Datas { get; }
public DArray(TArray type, List<DType> datas)
{
this.Type = type;
this.Datas = datas;
}
public override void Apply<T>(IDataActionVisitor<T> visitor, T x)
{
visitor.Accept(this, x);
}
public override void Apply<T1, T2>(IDataActionVisitor<T1, T2> visitor, T1 x, T2 y)
{
visitor.Accept(this, x, y);
}
public override TR Apply<TR>(IDataFuncVisitor<TR> visitor)
{
return visitor.Accept(this);
}
public override TR Apply<T, TR>(IDataFuncVisitor<T, TR> visitor, T x)
{
return visitor.Accept(this, x);
}
}
}

View File

@ -0,0 +1,48 @@
using Luban.Job.Cfg.DataVisitors;
using Luban.Job.Cfg.Defs;
using System.Collections.Generic;
namespace Luban.Job.Cfg.Datas
{
public class DBean : DType
{
public DefBean Type { get; }
public DefBean ImplType { get; }
public List<DType> Fields { get; }
public DBean(DefBean defType, DefBean implType, List<DType> fields)
{
this.Type = defType;
this.ImplType = implType;
this.Fields = fields;
}
public DType GetField(string fieldName)
{
ImplType.TryGetField(fieldName, out var _, out var findex);
return Fields[findex];
}
public override void Apply<T>(IDataActionVisitor<T> visitor, T x)
{
visitor.Accept(this, x);
}
public override void Apply<T1, T2>(IDataActionVisitor<T1, T2> visitor, T1 x, T2 y)
{
visitor.Accept(this, x, y);
}
public override TR Apply<TR>(IDataFuncVisitor<TR> visitor)
{
return visitor.Accept(this);
}
public override TR Apply<T, TR>(IDataFuncVisitor<T, TR> visitor, T x)
{
return visitor.Accept(this, x);
}
}
}

View File

@ -0,0 +1,41 @@
using Luban.Job.Cfg.DataVisitors;
namespace Luban.Job.Cfg.Datas
{
public class DBool : DType<bool>
{
public DBool(bool x) : base(x)
{
}
public override void Apply<T>(IDataActionVisitor<T> visitor, T x)
{
visitor.Accept(this, x);
}
public override void Apply<T1, T2>(IDataActionVisitor<T1, T2> visitor, T1 x, T2 y)
{
visitor.Accept(this, x, y);
}
public override TR Apply<TR>(IDataFuncVisitor<TR> visitor)
{
return visitor.Accept(this);
}
public override TR Apply<T, TR>(IDataFuncVisitor<T, TR> visitor, T x)
{
return visitor.Accept(this, x);
}
public override bool Equals(object obj)
{
return obj is DBool o && o.Value == this.Value;
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
}

View File

@ -0,0 +1,41 @@
using Luban.Job.Cfg.DataVisitors;
namespace Luban.Job.Cfg.Datas
{
public class DByte : DType<byte>
{
public DByte(byte x) : base(x)
{
}
public override void Apply<T>(IDataActionVisitor<T> visitor, T x)
{
visitor.Accept(this, x);
}
public override void Apply<T1, T2>(IDataActionVisitor<T1, T2> visitor, T1 x, T2 y)
{
visitor.Accept(this, x, y);
}
public override TR Apply<TR>(IDataFuncVisitor<TR> visitor)
{
return visitor.Accept(this);
}
public override TR Apply<T, TR>(IDataFuncVisitor<T, TR> visitor, T x)
{
return visitor.Accept(this, x);
}
public override bool Equals(object obj)
{
return obj is DByte o && o.Value == this.Value;
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
}

View File

@ -0,0 +1,31 @@
using Luban.Job.Cfg.DataVisitors;
namespace Luban.Job.Cfg.Datas
{
public class DBytes : DType<byte[]>
{
public DBytes(byte[] x) : base(x)
{
}
public override void Apply<T>(IDataActionVisitor<T> visitor, T x)
{
visitor.Accept(this, x);
}
public override void Apply<T1, T2>(IDataActionVisitor<T1, T2> visitor, T1 x, T2 y)
{
visitor.Accept(this, x, y);
}
public override TR Apply<TR>(IDataFuncVisitor<TR> visitor)
{
return visitor.Accept(this);
}
public override TR Apply<T, TR>(IDataFuncVisitor<T, TR> visitor, T x)
{
return visitor.Accept(this, x);
}
}
}

View File

@ -0,0 +1,38 @@
using Luban.Job.Cfg.DataVisitors;
using System;
namespace Luban.Job.Cfg.Datas
{
public class DDateTime : DType
{
public DateTime Time { get; }
public int UnixTime { get; }
public DDateTime(DateTime time)
{
this.Time = time;
this.UnixTime = (int)new DateTimeOffset(time).ToUnixTimeSeconds();
}
public override void Apply<T>(IDataActionVisitor<T> visitor, T x)
{
visitor.Accept(this, x);
}
public override void Apply<T1, T2>(IDataActionVisitor<T1, T2> visitor, T1 x, T2 y)
{
visitor.Accept(this, x, y);
}
public override TR Apply<TR>(IDataFuncVisitor<TR> visitor)
{
return visitor.Accept(this);
}
public override TR Apply<T, TR>(IDataFuncVisitor<T, TR> visitor, T x)
{
return visitor.Accept(this, x);
}
}
}

View File

@ -0,0 +1,41 @@
using Luban.Job.Cfg.DataVisitors;
namespace Luban.Job.Cfg.Datas
{
public class DDouble : DType<double>
{
public DDouble(double x) : base(x)
{
}
public override void Apply<T>(IDataActionVisitor<T> visitor, T x)
{
visitor.Accept(this, x);
}
public override void Apply<T1, T2>(IDataActionVisitor<T1, T2> visitor, T1 x, T2 y)
{
visitor.Accept(this, x, y);
}
public override TR Apply<TR>(IDataFuncVisitor<TR> visitor)
{
return visitor.Accept(this);
}
public override TR Apply<T, TR>(IDataFuncVisitor<T, TR> visitor, T x)
{
return visitor.Accept(this, x);
}
public override bool Equals(object obj)
{
return obj is DDouble o && o.Value == this.Value;
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
}

View File

@ -0,0 +1,52 @@
using Luban.Job.Cfg.DataVisitors;
using Luban.Job.Common.Types;
namespace Luban.Job.Cfg.Datas
{
public class DEnum : DType
{
public int Value { get; }
public string StrValue { get; }
public TEnum Type { get; }
public DEnum(TEnum type, string value)
{
Type = type;
StrValue = value;
Value = type.DefineEnum.GetValueByNameOrAlias(value);
}
public override void Apply<T>(IDataActionVisitor<T> visitor, T x)
{
visitor.Accept(this, x);
}
public override void Apply<T1, T2>(IDataActionVisitor<T1, T2> visitor, T1 x, T2 y)
{
visitor.Accept(this, x, y);
}
public override TR Apply<TR>(IDataFuncVisitor<TR> visitor)
{
return visitor.Accept(this);
}
public override TR Apply<T, TR>(IDataFuncVisitor<T, TR> visitor, T x)
{
return visitor.Accept(this, x);
}
public override bool Equals(object obj)
{
return obj is DEnum o && o.StrValue == this.StrValue;
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
}

View File

@ -0,0 +1,41 @@
using Luban.Job.Cfg.DataVisitors;
namespace Luban.Job.Cfg.Datas
{
public class DFint : DType<int>
{
public DFint(int x) : base(x)
{
}
public override void Apply<T>(IDataActionVisitor<T> visitor, T x)
{
visitor.Accept(this, x);
}
public override void Apply<T1, T2>(IDataActionVisitor<T1, T2> visitor, T1 x, T2 y)
{
visitor.Accept(this, x, y);
}
public override TR Apply<TR>(IDataFuncVisitor<TR> visitor)
{
return visitor.Accept(this);
}
public override TR Apply<T, TR>(IDataFuncVisitor<T, TR> visitor, T x)
{
return visitor.Accept(this, x);
}
public override bool Equals(object obj)
{
return obj is DInt o && o.Value == this.Value;
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
}

View File

@ -0,0 +1,41 @@
using Luban.Job.Cfg.DataVisitors;
namespace Luban.Job.Cfg.Datas
{
public class DFloat : DType<float>
{
public DFloat(float x) : base(x)
{
}
public override void Apply<T>(IDataActionVisitor<T> visitor, T x)
{
visitor.Accept(this, x);
}
public override void Apply<T1, T2>(IDataActionVisitor<T1, T2> visitor, T1 x, T2 y)
{
visitor.Accept(this, x, y);
}
public override TR Apply<TR>(IDataFuncVisitor<TR> visitor)
{
return visitor.Accept(this);
}
public override TR Apply<T, TR>(IDataFuncVisitor<T, TR> visitor, T x)
{
return visitor.Accept(this, x);
}
public override bool Equals(object obj)
{
return obj is DFloat o && o.Value == this.Value;
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
}

View File

@ -0,0 +1,41 @@
using Luban.Job.Cfg.DataVisitors;
namespace Luban.Job.Cfg.Datas
{
public class DFlong : DType<long>
{
public DFlong(long x) : base(x)
{
}
public override void Apply<T>(IDataActionVisitor<T> visitor, T x)
{
visitor.Accept(this, x);
}
public override void Apply<T1, T2>(IDataActionVisitor<T1, T2> visitor, T1 x, T2 y)
{
visitor.Accept(this, x, y);
}
public override TR Apply<TR>(IDataFuncVisitor<TR> visitor)
{
return visitor.Accept(this);
}
public override TR Apply<T, TR>(IDataFuncVisitor<T, TR> visitor, T x)
{
return visitor.Accept(this, x);
}
public override bool Equals(object obj)
{
return obj is DFlong o && o.Value == this.Value;
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
}

View File

@ -0,0 +1,41 @@
using Luban.Job.Cfg.DataVisitors;
namespace Luban.Job.Cfg.Datas
{
public class DFshort : DType<short>
{
public DFshort(short x) : base(x)
{
}
public override void Apply<T>(IDataActionVisitor<T> visitor, T x)
{
visitor.Accept(this, x);
}
public override void Apply<T1, T2>(IDataActionVisitor<T1, T2> visitor, T1 x, T2 y)
{
visitor.Accept(this, x, y);
}
public override TR Apply<TR>(IDataFuncVisitor<TR> visitor)
{
return visitor.Accept(this);
}
public override TR Apply<T, TR>(IDataFuncVisitor<T, TR> visitor, T x)
{
return visitor.Accept(this, x);
}
public override bool Equals(object obj)
{
return obj is DFshort o && o.Value == this.Value;
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
}

View File

@ -0,0 +1,41 @@
using Luban.Job.Cfg.DataVisitors;
namespace Luban.Job.Cfg.Datas
{
public class DInt : DType<int>
{
public DInt(int x) : base(x)
{
}
public override void Apply<T>(IDataActionVisitor<T> visitor, T x)
{
visitor.Accept(this, x);
}
public override void Apply<T1, T2>(IDataActionVisitor<T1, T2> visitor, T1 x, T2 y)
{
visitor.Accept(this, x, y);
}
public override TR Apply<TR>(IDataFuncVisitor<TR> visitor)
{
return visitor.Accept(this);
}
public override TR Apply<T, TR>(IDataFuncVisitor<T, TR> visitor, T x)
{
return visitor.Accept(this, x);
}
public override bool Equals(object obj)
{
return obj is DInt o && o.Value == this.Value;
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
}

View File

@ -0,0 +1,50 @@
using Luban.Job.Cfg.DataVisitors;
using Luban.Job.Common.Types;
using System.Collections.Generic;
namespace Luban.Job.Cfg.Datas
{
public class DList : DType
{
public TList Type { get; }
public List<DType> Datas { get; }
public DList(TList type, List<DType> datas)
{
this.Type = type;
this.Datas = datas;
}
public override void Apply<T>(IDataActionVisitor<T> visitor, T x)
{
visitor.Accept(this, x);
}
public override void Apply<T1, T2>(IDataActionVisitor<T1, T2> visitor, T1 x, T2 y)
{
visitor.Accept(this, x, y);
}
public override TR Apply<TR>(IDataFuncVisitor<TR> visitor)
{
return visitor.Accept(this);
}
public override TR Apply<T, TR>(IDataFuncVisitor<T, TR> visitor, T x)
{
return visitor.Accept(this, x);
}
//public override bool Equals(object obj)
//{
// if (obj is DList o)
// {
// return o.Datas.Count == this.Datas.Count && o.Datas.SequenceEqual(this.Datas);
// }
// else
// {
// return false;
// }
//}
}
}

View File

@ -0,0 +1,41 @@
using Luban.Job.Cfg.DataVisitors;
namespace Luban.Job.Cfg.Datas
{
public class DLong : DType<long>
{
public DLong(long x) : base(x)
{
}
public override void Apply<T>(IDataActionVisitor<T> visitor, T x)
{
visitor.Accept(this, x);
}
public override void Apply<T1, T2>(IDataActionVisitor<T1, T2> visitor, T1 x, T2 y)
{
visitor.Accept(this, x, y);
}
public override TR Apply<TR>(IDataFuncVisitor<TR> visitor)
{
return visitor.Accept(this);
}
public override TR Apply<T, TR>(IDataFuncVisitor<T, TR> visitor, T x)
{
return visitor.Accept(this, x);
}
public override bool Equals(object obj)
{
return obj is DLong o && o.Value == this.Value;
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
}

View File

@ -0,0 +1,48 @@
using Luban.Job.Cfg.DataVisitors;
using Luban.Job.Common.Types;
using System;
using System.Collections.Generic;
namespace Luban.Job.Cfg.Datas
{
public class DMap : DType
{
public TMap Type { get; }
public Dictionary<DType, DType> Datas { get; }
public DMap(TMap type, Dictionary<DType, DType> datas)
{
this.Type = type;
this.Datas = datas;
var set = new HashSet<DType>();
foreach (var key in datas.Keys)
{
if (!set.Add(key))
{
throw new Exception($"set 的 value:{key} 重复");
}
}
}
public override void Apply<T>(IDataActionVisitor<T> visitor, T x)
{
visitor.Accept(this, x);
}
public override void Apply<T1, T2>(IDataActionVisitor<T1, T2> visitor, T1 x, T2 y)
{
visitor.Accept(this, x, y);
}
public override TR Apply<TR>(IDataFuncVisitor<TR> visitor)
{
return visitor.Accept(this);
}
public override TR Apply<T, TR>(IDataFuncVisitor<T, TR> visitor, T x)
{
return visitor.Accept(this, x);
}
}
}

View File

@ -0,0 +1,48 @@
using Luban.Job.Cfg.DataVisitors;
using Luban.Job.Common.Types;
using System;
using System.Collections.Generic;
namespace Luban.Job.Cfg.Datas
{
public class DSet : DType
{
public TSet Type { get; }
public List<DType> Datas { get; }
public DSet(TSet type, List<DType> datas)
{
this.Type = type;
this.Datas = datas;
var set = new HashSet<DType>();
foreach (var data in datas)
{
if (!set.Add(data))
{
throw new Exception($"set 的 value:{data} 重复");
}
}
}
public override void Apply<T>(IDataActionVisitor<T> visitor, T x)
{
visitor.Accept(this, x);
}
public override void Apply<T1, T2>(IDataActionVisitor<T1, T2> visitor, T1 x, T2 y)
{
visitor.Accept(this, x, y);
}
public override TR Apply<TR>(IDataFuncVisitor<TR> visitor)
{
return visitor.Accept(this);
}
public override TR Apply<T, TR>(IDataFuncVisitor<T, TR> visitor, T x)
{
return visitor.Accept(this, x);
}
}
}

View File

@ -0,0 +1,41 @@
using Luban.Job.Cfg.DataVisitors;
namespace Luban.Job.Cfg.Datas
{
public class DShort : DType<short>
{
public DShort(short x) : base(x)
{
}
public override void Apply<T>(IDataActionVisitor<T> visitor, T x)
{
visitor.Accept(this, x);
}
public override void Apply<T1, T2>(IDataActionVisitor<T1, T2> visitor, T1 x, T2 y)
{
visitor.Accept(this, x, y);
}
public override TR Apply<TR>(IDataFuncVisitor<TR> visitor)
{
return visitor.Accept(this);
}
public override TR Apply<T, TR>(IDataFuncVisitor<T, TR> visitor, T x)
{
return visitor.Accept(this, x);
}
public override bool Equals(object obj)
{
return obj is DShort o && o.Value == this.Value;
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
}

View File

@ -0,0 +1,41 @@
using Luban.Job.Cfg.DataVisitors;
namespace Luban.Job.Cfg.Datas
{
public class DString : DType<string>
{
public DString(string x) : base(x)
{
}
public override void Apply<T>(IDataActionVisitor<T> visitor, T x)
{
visitor.Accept(this, x);
}
public override void Apply<T1, T2>(IDataActionVisitor<T1, T2> visitor, T1 x, T2 y)
{
visitor.Accept(this, x, y);
}
public override TR Apply<TR>(IDataFuncVisitor<TR> visitor)
{
return visitor.Accept(this);
}
public override TR Apply<T, TR>(IDataFuncVisitor<T, TR> visitor, T x)
{
return visitor.Accept(this, x);
}
public override bool Equals(object obj)
{
return obj is DString o && o.Value == this.Value;
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
}

View File

@ -0,0 +1,41 @@
using Luban.Job.Cfg.DataVisitors;
namespace Luban.Job.Cfg.Datas
{
public class DText : DType<string>
{
public DText(string x) : base(x)
{
}
public override void Apply<T>(IDataActionVisitor<T> visitor, T x)
{
visitor.Accept(this, x);
}
public override void Apply<T1, T2>(IDataActionVisitor<T1, T2> visitor, T1 x, T2 y)
{
visitor.Accept(this, x, y);
}
public override TR Apply<TR>(IDataFuncVisitor<TR> visitor)
{
return visitor.Accept(this);
}
public override TR Apply<T, TR>(IDataFuncVisitor<T, TR> visitor, T x)
{
return visitor.Accept(this, x);
}
public override bool Equals(object obj)
{
return obj is DText o && o.Value == this.Value;
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
}

View File

@ -0,0 +1,39 @@
using Luban.Job.Cfg.DataVisitors;
using System.Text;
namespace Luban.Job.Cfg.Datas
{
public abstract class DType
{
public abstract void Apply<T>(IDataActionVisitor<T> visitor, T x);
public abstract void Apply<T1, T2>(IDataActionVisitor<T1, T2> visitor, T1 x, T2 y);
public abstract TR Apply<TR>(IDataFuncVisitor<TR> visitor);
public abstract TR Apply<T, TR>(IDataFuncVisitor<T, TR> visitor, T x);
public override string ToString()
{
var s = new StringBuilder();
this.Apply(ToStringVisitor.Ins, s);
return s.ToString();
}
/// <summary>
/// 从哪儿创建的. 一般来说会保存它的源文件
/// </summary>
public object Source { get; set; }
}
public abstract class DType<T> : DType
{
public T Value { get; }
protected DType(T value)
{
Value = value;
}
}
}

View File

@ -0,0 +1,42 @@
using Luban.Job.Cfg.DataVisitors;
using System.Numerics;
namespace Luban.Job.Cfg.Datas
{
public class DVector2 : DType<Vector2>
{
public DVector2(Vector2 x) : base(x)
{
}
public override void Apply<T>(IDataActionVisitor<T> visitor, T x)
{
visitor.Accept(this, x);
}
public override void Apply<T1, T2>(IDataActionVisitor<T1, T2> visitor, T1 x, T2 y)
{
visitor.Accept(this, x, y);
}
public override TR Apply<TR>(IDataFuncVisitor<TR> visitor)
{
return visitor.Accept(this);
}
public override TR Apply<T, TR>(IDataFuncVisitor<T, TR> visitor, T x)
{
return visitor.Accept(this, x);
}
public override bool Equals(object obj)
{
return obj is DVector2 o && o.Value == this.Value;
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
}

View File

@ -0,0 +1,42 @@
using Luban.Job.Cfg.DataVisitors;
using System.Numerics;
namespace Luban.Job.Cfg.Datas
{
public class DVector3 : DType<Vector3>
{
public DVector3(Vector3 x) : base(x)
{
}
public override void Apply<T>(IDataActionVisitor<T> visitor, T x)
{
visitor.Accept(this, x);
}
public override void Apply<T1, T2>(IDataActionVisitor<T1, T2> visitor, T1 x, T2 y)
{
visitor.Accept(this, x, y);
}
public override TR Apply<TR>(IDataFuncVisitor<TR> visitor)
{
return visitor.Accept(this);
}
public override TR Apply<T, TR>(IDataFuncVisitor<T, TR> visitor, T x)
{
return visitor.Accept(this, x);
}
public override bool Equals(object obj)
{
return obj is DVector3 o && o.Value == this.Value;
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
}

View File

@ -0,0 +1,42 @@
using Luban.Job.Cfg.DataVisitors;
using System.Numerics;
namespace Luban.Job.Cfg.Datas
{
public class DVector4 : DType<Vector4>
{
public DVector4(Vector4 x) : base(x)
{
}
public override void Apply<T>(IDataActionVisitor<T> visitor, T x)
{
visitor.Accept(this, x);
}
public override void Apply<T1, T2>(IDataActionVisitor<T1, T2> visitor, T1 x, T2 y)
{
visitor.Accept(this, x, y);
}
public override TR Apply<TR>(IDataFuncVisitor<TR> visitor)
{
return visitor.Accept(this);
}
public override TR Apply<T, TR>(IDataFuncVisitor<T, TR> visitor, T x)
{
return visitor.Accept(this, x);
}
public override bool Equals(object obj)
{
return obj is DVector4 o && o.Value == this.Value;
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
}

View File

@ -0,0 +1,349 @@
using Luban.Common.Utils;
using Luban.Config.Common.RawDefs;
using Luban.Job.Common.Defs;
using Luban.Job.Common.RawDefs;
using Luban.Server.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Luban.Job.Cfg.Defs
{
class CfgDefLoader : CommonDefLoader
{
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
private readonly List<Table> _cfgTables = new List<Table>();
private readonly List<Service> _cfgServices = new List<Service>();
private readonly List<Group> _cfgGroups = new List<Group>();
private readonly List<string> _defaultGroups = new List<string>();
public CfgDefLoader(RemoteAgent agent) : base(agent)
{
RegisterRootDefineHandler("service", AddService);
RegisterRootDefineHandler("group", AddGroup);
RegisterModuleDefineHandler("table", AddTable);
IsBeanFieldMustDefineId = false;
}
public Defines BuildDefines()
{
return new Defines()
{
TopModule = TopModule,
Consts = this._consts,
Enums = _enums,
Beans = _beans,
Tables = _cfgTables,
Services = _cfgServices,
Groups = _cfgGroups,
};
}
private static readonly List<string> _groupOptionalAttrs = new List<string> { "default" };
private static readonly List<string> _groupRequireAttrs = new List<string> { "name" };
private void AddGroup(XElement e)
{
ValidAttrKeys(e, _groupOptionalAttrs, _groupRequireAttrs);
List<string> groupNames = CreateGroups(e.Attribute("name").Value);
foreach (var g in groupNames)
{
if (_cfgGroups.Any(cg => cg.Names.Contains(g)))
{
throw new Exception($"group名:{g} 重复");
}
}
if (XmlUtil.GetOptionBoolAttribute(e, "default"))
{
this._defaultGroups.AddRange(groupNames);
}
_cfgGroups.Add(new Group() { Names = groupNames });
}
private void FillValueValidator(CfgField f, XAttribute e, string validatorName)
{
if (e != null)
{
var validator = new Validator() { Type = validatorName, Rule = e.Value };
f.Validators.Add(validator);
f.ValueValidators.Add(validator);
}
}
private void FillValidators(XElement e, string key, List<Validator> result)
{
var attr = e.Attribute(key);
if (attr != null)
{
foreach (var validatorStr in attr.Value.Split('#', StringSplitOptions.RemoveEmptyEntries))
{
var sepIndex = validatorStr.IndexOf(':');
if (sepIndex < 0)
{
throw new Exception($"定义文件:{CurImportFile} 类型:{CurDefine} 字段:{e} key:{key} 不是合法的 validator 定义 (key1:value1#key2:value2 ...)");
}
result.Add(new Validator() { Type = validatorStr[..sepIndex], Rule = validatorStr[(sepIndex + 1)..] });
}
}
}
private readonly List<string> _serviceAttrs = new List<string> { "name", "manager", "group" };
private void AddService(XElement e)
{
var name = XmlUtil.GetRequiredAttribute(e, "name");
var manager = XmlUtil.GetRequiredAttribute(e, "manager");
List<string> groups = CreateGroups(XmlUtil.GetOptionalAttribute(e, "group"));
var refs = new List<string>();
s_logger.Trace("service name:{name} manager:{manager}", name, manager);
ValidAttrKeys(e, _serviceAttrs, _serviceAttrs);
foreach (XElement ele in e.Elements())
{
string tagName = ele.Name.LocalName;
s_logger.Trace("service {service_name} tag: {name} {value}", name, tagName, ele);
switch (tagName)
{
case "ref":
{
refs.Add(XmlUtil.GetRequiredAttribute(ele, "name"));
break;
}
default:
{
throw new Exception($"service:{name} tag:{tagName} 非法");
}
}
}
if (!ValidGroup(groups, out var invalidGroup))
{
throw new Exception($"service:{name} group:{invalidGroup} 不存在");
}
_cfgServices.Add(new Service() { Name = name, Manager = manager, Groups = groups, Refs = refs });
}
private readonly List<string> _tableOptionalAttrs = new List<string> { "index", "mode", "group" };
private readonly List<string> _tableRequireAttrs = new List<string> { "name", "value", "input" };
private readonly Dictionary<string, Table> _name2CfgTable = new Dictionary<string, Table>();
private static List<string> CreateGroups(string s)
{
return s.Split(',', ';').Select(x => x.Trim()).Where(x => !string.IsNullOrWhiteSpace(x)).ToList();
}
private bool ValidGroup(List<string> groups, out string invalidGroup)
{
foreach (var g in groups)
{
if (!this._cfgGroups.Any(cg => cg.Names.Contains(g)))
{
invalidGroup = g;
return false;
}
}
invalidGroup = null;
return true;
}
private ETableMode ConvertMode(string tableName, string modeStr, string indexStr)
{
ETableMode mode;
switch (modeStr)
{
case "one":
{
if (!string.IsNullOrWhiteSpace(indexStr))
{
throw new Exception($"定义文件:{CurImportFile} table:{tableName} mode=one 是单例表不支持定义index属性");
}
mode = ETableMode.ONE;
break;
}
case "map":
{
if ((string.IsNullOrWhiteSpace(indexStr) || indexStr.Split(',').Length != 1))
{
throw new Exception($"定义文件:{CurImportFile} table:{tableName} 是单键表必须在index属性里指定1个key");
}
mode = ETableMode.MAP;
break;
}
case "bmap":
{
if ((string.IsNullOrWhiteSpace(indexStr) || indexStr.Split(',').Length != 2))
{
throw new Exception($"定义文件:{CurImportFile} table:{tableName} 是双键表必须在index属性里指定2个key");
}
mode = ETableMode.BMAP;
break;
}
case "":
{
// 当 mode 属性为空时, 智能根据 index 值推测表类型
// 如果index为空或一个键,则为 MAP类型
// 如果index为2个键则为 BMAP类型
var indexs = indexStr.Split(',').Select(s => s.Trim()).ToList();
switch (indexs.Count)
{
case 0:
case 1: mode = ETableMode.MAP; break;
case 2: mode = ETableMode.BMAP; break;
default: throw new Exception($"定义文件:{CurImportFile} table:{tableName} 最多只能有两个 index");
}
break;
}
default:
{
throw new ArgumentException($"不支持的 mode:{modeStr}");
}
}
return mode;
}
private void AddTable(XElement e)
{
ValidAttrKeys(e, _tableOptionalAttrs, _tableRequireAttrs);
var p = new Table()
{
Name = XmlUtil.GetRequiredAttribute(e, "name"),
Namespace = CurNamespace,
ValueType = XmlUtil.GetRequiredAttribute(e, "value"),
Index = XmlUtil.GetOptionalAttribute(e, "index"),
Groups = CreateGroups(XmlUtil.GetOptionalAttribute(e, "group")),
};
p.Mode = ConvertMode(p.Name, XmlUtil.GetOptionalAttribute(e, "mode"), p.Index);
if (p.Groups.Count == 0)
{
p.Groups = this._defaultGroups;
}
else if (!ValidGroup(p.Groups, out var invalidGroup))
{
throw new Exception($"定义文件:{CurImportFile} table:{p.Name} group:{invalidGroup} 不存在");
}
p.InputFiles.AddRange(XmlUtil.GetRequiredAttribute(e, "input").Split(','));
if (!_name2CfgTable.TryAdd(p.Name, p))
{
var exist = _name2CfgTable[p.Name];
throw new Exception($"定义文件:{CurImportFile} table:{p.Namespace}.{p.Name} 与 {exist.Namespace}.{exist.Name} 重复");
}
_cfgTables.Add(p);
}
private static readonly List<string> _fieldOptionalAttrs = new List<string> {
"index", "sep", "validator", "key_validator", "value_validator",
"ref", "path", "range", "multi_rows", "group", "res", "convert" };
private static readonly List<string> _fieldRequireAttrs = new List<string> { "name", "type" };
protected override Field CreateField(XElement e)
{
ValidAttrKeys(e, _fieldOptionalAttrs, _fieldRequireAttrs);
var f = new CfgField()
{
Name = XmlUtil.GetRequiredAttribute(e, "name"),
Index = XmlUtil.GetOptionalAttribute(e, "index"),
Sep = XmlUtil.GetOptionalAttribute(e, "sep"),
IsMultiRow = XmlUtil.GetOptionBoolAttribute(e, "multi_rows"),
Groups = CreateGroups(XmlUtil.GetOptionalAttribute(e, "group")),
Resource = XmlUtil.GetOptionalAttribute(e, "res"),
Converter = XmlUtil.GetOptionalAttribute(e, "convert"),
};
// 字段与table的默认组不一样。
// table 默认只属于default=1的组
// 字段默认属于所有组
if (f.Groups.Count == 0)
{
}
else if (!ValidGroup(f.Groups, out var invalidGroup))
{
throw new Exception($"定义文件:{CurImportFile} field:{e} group:{invalidGroup} 不存在");
}
f.Type = CreateType(e, "type");
FillValueValidator(f, e.Attribute("ref"), "ref");
FillValueValidator(f, e.Attribute("path"), "path"); // (ue4|normal|regex);xxx;xxx
FillValueValidator(f, e.Attribute("range"), "range");
FillValidators(e, "key_validator", f.KeyValidators);
FillValidators(e, "value_validator", f.ValueValidators);
FillValidators(e, "validator", f.Validators);
return f;
}
private static readonly List<string> _beanOptinsAttrs = new List<string> { "value_type", "alias", "sep" };
private static readonly List<string> _beanRequireAttrs = new List<string> { "name" };
protected override void AddBean(XElement e, string parent)
{
ValidAttrKeys(e, _beanOptinsAttrs, _beanRequireAttrs);
var b = new CfgBean()
{
Name = XmlUtil.GetRequiredAttribute(e, "name"),
Namespace = CurNamespace,
Parent = parent.Length > 0 ? parent : "",
TypeId = 0,
IsSerializeCompatible = true,
IsValueType = XmlUtil.GetOptionBoolAttribute(e, "value_type"),
Alias = XmlUtil.GetOptionalAttribute(e, "alias"),
Sep = XmlUtil.GetOptionalAttribute(e, "sep"),
};
var childBeans = new List<XElement>();
bool defineAnyChildBean = false;
foreach (XElement fe in e.Elements())
{
switch (fe.Name.LocalName)
{
case "var":
{
if (defineAnyChildBean)
{
throw new LoadDefException($"定义文件:{CurImportFile} 类型:{b.FullName} 的多态子bean必须在所有成员字段 <var> 之前定义");
}
b.Fields.Add(CreateField(fe)); ;
break;
}
case "bean":
{
defineAnyChildBean = true;
childBeans.Add(fe);
break;
}
default:
{
throw new LoadDefException($"定义文件:{CurImportFile} 类型:{b.FullName} 不支持 tag:{fe.Name}");
}
}
}
s_logger.Trace("add bean:{@bean}", b);
_beans.Add(b);
var fullname = b.FullName;
foreach (var cb in childBeans)
{
AddBean(cb, fullname);
}
}
}
}

View File

@ -0,0 +1,30 @@
using Luban.Common.Utils;
using Luban.Job.Common.Defs;
using Luban.Job.Common.Utils;
namespace Luban.Job.Cfg.Defs
{
public abstract class CfgDefTypeBase : DefTypeBase
{
public DefAssembly Assembly => (DefAssembly)AssemblyBase;
public virtual string UeBpName => "U" + Name;
public virtual string UeBpFullName => TypeUtil.MakeCppJoinedFullName("U" + Namespace, Name);
public string UeBpHeaderFileName => "bp_" + RenderFileUtil.GetUeCppDefTypeHeaderFilePath(FullName);
public string UeBpHeaderFileNameWithoutSuffix => "bp_" + RenderFileUtil.GetUeCppDefTypeHeaderFilePathWithoutSuffix(FullName);
public string EditorUeFullName => TypeUtil.MakeCppFullName(Namespace, Name);
public string UeFname => "F" + Name;
public string UeFfullName => TypeUtil.MakeCppFullName(Namespace, UeFname);
public string UeHeaderFileName => RenderFileUtil.GetUeCppDefTypeHeaderFilePath(FullName);
public string UeEditorHeaderFileName => "editor_" + RenderFileUtil.GetUeCppDefTypeHeaderFilePath(FullName);
}
}

View File

@ -0,0 +1,233 @@
using Luban.Config.Common.RawDefs;
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.TypeVisitors;
using Luban.Job.Common.Defs;
using Luban.Server.Common;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace Luban.Job.Cfg.Defs
{
public class DefAssembly : DefAssemblyBase
{
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
public Service CfgTargetService { get; private set; }
public TimeZoneInfo TimeZone { get; }
public DefAssembly(TimeZoneInfo timezone)
{
this.TimeZone = timezone;
}
public bool NeedExport(List<string> groups)
{
if (groups.Count == 0)
{
return true;
}
return groups.Any(g => CfgTargetService.Groups.Contains(g));
}
private readonly List<Service> _cfgServices = new List<Service>();
private readonly ConcurrentDictionary<string, List<DType>> _recordsByTables = new ConcurrentDictionary<string, List<DType>>();
private readonly ConcurrentDictionary<string, Dictionary<DType, DBean>> _recordsMapByTables = new ConcurrentDictionary<string, Dictionary<DType, DBean>>();
public Dictionary<string, DefTable> CfgTables { get; } = new Dictionary<string, DefTable>();
public void AddCfgTable(DefTable table)
{
if (!CfgTables.TryAdd(table.FullName, table))
{
throw new Exception($"table:{table.FullName} duplicated");
}
}
public DefTable GetCfgTable(string name)
{
return CfgTables.TryGetValue(name, out var t) ? t : null;
}
public void AddDataTable(DefTable table, List<DType> records)
{
_recordsByTables[table.FullName] = records;
}
public void SetDataTableMap(DefTable table, Dictionary<DType, DBean> recordMap)
{
_recordsMapByTables[table.FullName] = recordMap;
}
public List<DType> GetTableDataList(DefTable table)
{
return _recordsByTables[table.FullName];
}
public Dictionary<DType, DBean> GetTableDataMap(DefTable table)
{
return _recordsMapByTables.GetValueOrDefault(table.FullName);
}
public List<DefTable> GetExportTables()
{
return Types.Values.Where(t => t is DefTable ct && ct.NeedExport).Select(t => (DefTable)t).ToList();
}
public List<DefTypeBase> GetExportTypes()
{
var refTypes = new Dictionary<string, DefTypeBase>();
var targetService = CfgTargetService;
foreach (var refType in targetService.Refs)
{
if (!this.Types.ContainsKey(refType))
{
throw new Exception($"service:{targetService.Name} ref:{refType} 类型不存在");
}
if (!refTypes.TryAdd(refType, this.Types[refType]))
{
throw new Exception($"service:{targetService.Name} ref:{refType} 重复引用");
}
}
foreach ((var fullTypeName, var type) in this.Types)
{
if (!refTypes.ContainsKey(fullTypeName) && (type is DefConst || type is DefEnum))
{
refTypes.Add(fullTypeName, type);
}
}
foreach (var table in GetExportTables())
{
refTypes[table.FullName] = table;
table.ValueTType.Apply(RefTypeVisitor.Ins, refTypes);
}
return refTypes.Values.ToList();
}
public void Load(string outputService, Defines defines, RemoteAgent agent)
{
this.Agent = agent;
SupportDatetimeType = true;
TopModule = defines.TopModule;
CfgTargetService = defines.Services.Find(s => s.Name == outputService);
if (CfgTargetService == null)
{
throw new ArgumentException($"service:{outputService} not exists");
}
foreach (var c in defines.Consts)
{
AddType(new DefConst(c));
}
foreach (var e in defines.Enums)
{
AddType(new DefEnum(e));
}
foreach (var b in defines.Beans)
{
AddType(new DefBean((CfgBean)b));
}
foreach (var p in defines.Tables)
{
var table = new DefTable(p);
AddType(table);
AddCfgTable(table);
}
_cfgServices.AddRange(defines.Services);
foreach (var type in Types.Values)
{
type.AssemblyBase = this;
}
foreach (var type in Types.Values)
{
try
{
s_logger.Trace("precompile type:{0} begin", type.FullName);
type.PreCompile();
s_logger.Trace("precompile type:{0} end", type.FullName);
}
catch (Exception)
{
agent.Error("precompile type:{0} error", type.FullName);
throw;
}
}
foreach (var type in Types.Values)
{
try
{
s_logger.Trace("compile type:{0} begin", type.FullName);
type.Compile();
s_logger.Trace("compile type:{0} end", type.FullName);
}
catch (Exception)
{
agent.Error("compile type:{0} error", type.FullName);
s_logger.Error("compile type:{0} error", type.FullName);
throw;
}
}
foreach (var type in Types.Values)
{
try
{
s_logger.Trace("post compile type:{0} begin", type.FullName);
type.PostCompile();
s_logger.Trace("post compile type:{0} end", type.FullName);
}
catch (Exception)
{
agent.Error("post compile type:{0} error", type.FullName);
s_logger.Error("post compile type:{0} error", type.FullName);
throw;
}
}
// 丑陋. 怎么写更好?
// 递归 设置DefBean及DefField 的 IsMultiRow
var multiRowBeans = new HashSet<DefBean>();
for (bool anyMark = true; anyMark;)
{
anyMark = false;
foreach (var type in this.Types.Values)
{
if (type is DefBean beanType && !beanType.IsMultiRow)
{
bool isMultiRows;
if (beanType.IsNotAbstractType)
{
isMultiRows = beanType.HierarchyFields.Any(f => ((DefField)f).ComputeIsMultiRow());
}
else
{
isMultiRows = beanType.HierarchyNotAbstractChildren.Any(c => ((DefBean)c).IsMultiRow);
}
if (isMultiRows)
{
beanType.IsMultiRow = true;
//s_logger.Info("bean:{bean} is multi row", beanType.FullName);
anyMark = true;
}
}
}
}
}
}
}

View File

@ -0,0 +1,215 @@
using Luban.Common.Utils;
using Luban.Config.Common.RawDefs;
using Luban.Job.Cfg.TypeVisitors;
using Luban.Job.Common.Defs;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Luban.Job.Cfg.Defs
{
public class DefBean : DefBeanBase
{
public const string TYPE_NAME_KEY = "__type__";
public string Alias { get; }
public bool IsMultiRow { get; set; }
public String Sep { get; }
public List<DefField> HierarchyExportFields { get; private set; }
public List<DefField> ExportFields { get; private set; }
public bool IsDefineEquals(DefBean b)
{
return DeepCompareTypeDefine.Ins.Compare(this, b, new Dictionary<DefTypeBase, bool>(), new HashSet<DefTypeBase>());
}
public string GoImport
{
get
{
var imports = new HashSet<string>();
foreach (var f in Fields)
{
f.CType.Apply(CollectGoImport.Ins, imports);
}
return string.Join('\n', imports.Select(im => $"import \"{im}\""));
}
}
public string UeBpIncludes
{
get
{
//var imports = new HashSet<CfgDefTypeBase>();
//if (ParentDefType != null)
//{
// imports.Add(ParentDefType);
//}
//foreach (var f in Fields)
//{
// f.CType.Apply(CollectEditorCppHeaderIncludeVisitor.Ins, imports);
//}
//imports.Remove(this);
//return string.Join('\n', imports.Select(im => $"#include \"{im.UeBpHeaderFileName}\""));
throw new NotImplementedException();
}
}
//public string UeForwards
//{
// get
// {
// var imports = new HashSet<IDefType>();
// foreach (var f in Fields)
// {
// f.CType.Apply(CollectUeCppForwardDefineVisitor.Ins, imports);
// }
// return string.Join('\n', imports.Select(im => $"{im.CppNamespaceBegin} class {im.UeBpName}; {im.CppNamespaceEnd}"));
// }
//}
public string EditorCppIncludes
{
get
{
//var imports = new HashSet<CfgDefTypeBase>();
//if (ParentDefType != null)
//{
// imports.Add(ParentDefType);
//}
//foreach (var f in Fields)
//{
// f.CType.Apply(CollectEditorCppHeaderIncludeVisitor.Ins, imports);
//}
//imports.Remove(this);
//return string.Join('\n', imports.Select(im => $"#include \"{im.UeEditorHeaderFileName}\""));
throw new NotImplementedException();
}
}
public string EditorCppForwards
{
get
{
var imports = new HashSet<DefTypeBase>();
foreach (var f in Fields)
{
f.CType.Apply(CollectEditorCppForwardDefineVisitor.Ins, imports);
}
//return string.Join('\n', imports.Select(im => $"{im.CppNamespaceBegin} struct {im.UeFname}; {im.CppNamespaceEnd}"));
throw new NotImplementedException();
}
}
public DefBean(CfgBean b) : base(b)
{
Alias = b.Alias;
Id = b.TypeId;
Sep = b.Sep;
}
protected override DefFieldBase CreateField(Common.RawDefs.Field f, int idOffset)
{
return new DefField(this, (CfgField)f, idOffset);
}
internal DefField GetField(string index)
{
return (DefField)HierarchyFields.Where(f => f.Name == index).FirstOrDefault();
}
internal bool TryGetField(string index, out DefField field, out int fieldIndexId)
{
for (int i = 0; i < HierarchyFields.Count; i++)
{
if (HierarchyFields[i].Name == index)
{
field = (DefField)HierarchyFields[i];
fieldIndexId = i;
return true;
}
}
field = null;
fieldIndexId = 0;
return false;
}
public override DefBeanBase GetNotAbstractChildType(string typeNameOrAliasName)
{
if (string.IsNullOrWhiteSpace(typeNameOrAliasName))
{
return null;
}
foreach (DefBean c in HierarchyNotAbstractChildren)
{
if (c.Name == typeNameOrAliasName || c.Alias == typeNameOrAliasName)
{
return c;
}
}
return null;
}
public override void PreCompile()
{
if (!string.IsNullOrEmpty(Parent))
{
if ((ParentDefType = (DefBean)AssemblyBase.GetDefType(Namespace, Parent)) == null)
{
throw new Exception($"bean:{FullName} parent:{Parent} not exist");
}
if (ParentDefType.Children == null)
{
ParentDefType.Children = new List<DefBeanBase>();
}
ParentDefType.Children.Add(this);
}
CollectHierarchyFields(HierarchyFields);
this.ExportFields = this.Fields.Select(f => (DefField)f).Where(f => f.NeedExport).ToList();
this.HierarchyExportFields = this.HierarchyFields.Select(f => (DefField)f).Where(f => f.NeedExport).ToList();
}
public override void Compile()
{
var cs = new List<DefBeanBase>();
if (Children != null)
{
CollectHierarchyNotAbstractChildren(cs);
}
HierarchyNotAbstractChildren = cs;
if (Id != 0)
{
throw new Exception($"bean:{FullName} beanid:{Id} should be 0!");
}
else
{
Id = TypeUtil.ComputCfgHashIdByName(FullName);
}
// 检查别名是否重复
HashSet<string> nameOrAliasName = cs.Select(b => b.Name).ToHashSet();
foreach (DefBean c in cs)
{
if (!string.IsNullOrWhiteSpace(c.Alias) && !nameOrAliasName.Add(c.Alias))
{
throw new Exception($"bean:{FullName} alias:{c.Alias} 重复");
}
}
DefField.CompileFields(this, HierarchyFields, false);
}
public override void PostCompile()
{
foreach (var field in HierarchyFields)
{
field.PostCompile();
}
}
}
}

View File

@ -0,0 +1,300 @@
using Luban.Common.Utils;
using Luban.Config.Common.RawDefs;
using Luban.Job.Cfg.Validators;
using Luban.Job.Common.Defs;
using Luban.Job.Common.Types;
using Luban.Job.Common.TypeVisitors;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Luban.Job.Cfg.Defs
{
public class DefField : DefFieldBase
{
public DefAssembly Assembly => (DefAssembly)HostType.AssemblyBase;
public bool RawIsMultiRow { get; }
public bool IsMultiRow { get; private set; }
public bool ComputeIsMultiRow()
{
if (IsMultiRow)
{
return true;
}
switch (CType)
{
case TBean b: { return IsMultiRow = ((DefBean)b.Bean).IsMultiRow; }
case TList b: { return IsMultiRow = b.ElementType is TBean b2 && ((DefBean)b2.Bean).IsMultiRow; }
case TArray b: { return IsMultiRow = b.ElementType is TBean b2 && ((DefBean)b2.Bean).IsMultiRow; }
case TMap b: { return IsMultiRow = b.ValueType is TBean b2 && ((DefBean)b2.Bean).IsMultiRow; }
default: return false;
}
}
public string Index { get; }
public List<string> Groups { get; }
public DefField IndexField { get; private set; }
public RefValidator Ref { get; private set; }
// 对于 two key map, 需要检查 ref,但不为它生成 ref 代码.故只有map类型表才要生成代码
public bool GenRef => Ref != null && Assembly.GetCfgTable(Ref.FirstTable).IsMapTable;
public bool HasRecursiveRef => (CType is TBean)
|| (CType is TArray ta && ta.ElementType is TBean)
|| (CType is TList tl && tl.ElementType is TBean)
|| (CType is TMap tm && tm.ValueType is TBean);
public string CsRefTypeName
{
get
{
var table = Assembly.GetCfgTable(Ref.FirstTable);
return table.ValueTType.Apply(CsDefineTypeName.Ins);
}
}
public string CsRefValidatorDefine
{
get
{
var table = Assembly.GetCfgTable(Ref.FirstTable);
return $"{table.ValueTType.Apply(CsDefineTypeName.Ins)} {CsRefVarName};";
}
}
public string JavaRefTypeName
{
get
{
var table = Assembly.GetCfgTable(Ref.FirstTable);
return table.ValueTType.Apply(JavaDefineTypeName.Ins);
}
}
public string JavaRefValidatorDefine
{
get
{
var table = Assembly.GetCfgTable(Ref.FirstTable);
return $"{table.ValueTType.Apply(JavaDefineTypeName.Ins)} {JavaRefVarName};";
}
}
public string TsRefValidatorDefine
{
get
{
var table = Assembly.GetCfgTable(Ref.FirstTable);
return $"{TsRefVarName} : {table.ValueTType.Apply(TsDefineTypeName.Ins)};";
}
}
public string Sep { get; set; }
// 如果没有指定sep
// 如果是bean,且指定了sep则使用此值
// 如果是vectorN,使用 ,
public string ActualSep => string.IsNullOrWhiteSpace(Sep) ? (CType is TBean bean ? ((DefBean)bean.Bean).Sep : "") : Sep;
public List<IValidator> KeyValidators { get; } = new List<IValidator>();
public List<IValidator> ValueValidators { get; } = new List<IValidator>();
public List<IValidator> Validators { get; } = new List<IValidator>();
public string ResourceTag { get; }
public bool IsResource => !string.IsNullOrEmpty(ResourceTag);
public bool NeedMarshalBoolPrefix => CType.Apply(NeedMarshalBoolPrefixVisitor.Ins);
public string CsRefVarName => $"{CsStyleName}_Ref";
public string JavaRefVarName => $"{JavaStyleName}_Ref";
public string TsRefVarName => $"{TsStyleName}_Ref";
public string JavaGetterName => TypeUtil.ToJavaGetterName(Name);
public string CppGetterName => JavaGetterName;
public bool NeedExport => Assembly.NeedExport(this.Groups);
public TEnum Remapper { get; private set; }
public CfgField RawDefine { get; }
public DefField(DefTypeBase host, CfgField f, int idOffset) : base(host, f, idOffset)
{
Index = f.Index;
Sep = f.Sep;
this.IsMultiRow = this.RawIsMultiRow = f.IsMultiRow;
ResourceTag = f.Resource;
this.Validators.AddRange(f.Validators.Select(v => ValidatorFactory.Create(v)));
this.KeyValidators.AddRange(f.KeyValidators.Select(v => ValidatorFactory.Create(v)));
this.ValueValidators.AddRange(f.ValueValidators.Select(v => ValidatorFactory.Create(v)));
this.Groups = f.Groups;
this.RawDefine = f;
}
public override void Compile()
{
base.Compile();
foreach (var v in this.Validators)
{
v.Compile(this);
}
foreach (var v in this.KeyValidators)
{
v.Compile(this);
}
foreach (var v in this.ValueValidators)
{
v.Compile(this);
}
switch (CType)
{
case TArray t:
{
if (t.ElementType is TBean e && !e.IsDynamic && e.Bean.HierarchyFields.Count == 0)
{
throw new Exception($"container element type can't be empty bean");
}
break;
}
case TList t:
{
if (t.ElementType is TBean e && !e.IsDynamic && e.Bean.HierarchyFields.Count == 0)
{
throw new Exception($"container element type can't be empty bean");
}
break;
}
}
if (IsMultiRow && !CType.IsCollection)
{
throw new Exception($"只有容器类型才支持 multi_line 属性");
}
if (string.IsNullOrEmpty(Sep) && CType is TBean bean)
{
Sep = bean.GetBeanAs<DefBean>().Sep;
}
if (!string.IsNullOrEmpty(Index))
{
if ((CType is TArray tarray) && (tarray.ElementType is TBean b))
{
if ((IndexField = b.GetBeanAs<DefBean>().GetField(Index)) == null)
{
throw new Exception($"type:{HostType.FullName} field:{Name} index:{Index}. index not exist");
}
}
else if ((CType is TList tlist) && (tlist.ElementType is TBean tb))
{
if ((IndexField = tb.GetBeanAs<DefBean>().GetField(Index)) == null)
{
throw new Exception($"type:{HostType.FullName} field:{Name} index:{Index}. index not exist");
}
}
else
{
throw new Exception($"type:{HostType.FullName} field:{Name} index:{Index}. only array:bean or list:bean support index");
}
}
if (!CType.IsCollection && !(CType is TBean))
{
this.Ref = (RefValidator)this.Validators.FirstOrDefault(v => v is RefValidator);
}
if (!string.IsNullOrEmpty(this.RawDefine.Converter))
{
this.Remapper = AssemblyBase.GetDefTType(HostType.Namespace, this.RawDefine.Converter, this.IsNullable) as TEnum;
if (this.Remapper == null)
{
throw new Exception($"type:{HostType.FullName} field:{Name} converter:{this.RawDefine.Converter} not exists");
}
}
// 检查所引用的表是否导出了
if (NeedExport)
{
var allValidators = new List<IValidator>(this.Validators);
allValidators.AddRange(this.KeyValidators);
allValidators.AddRange(this.ValueValidators);
foreach (var val in allValidators)
{
if (val is RefValidator refValidator && !Assembly.GetCfgTable(refValidator.FirstTable).NeedExport)
{
throw new Exception($"type:{HostType.FullName} field:{Name} ref 引用的表:{refValidator.FirstTable} 没有导出");
}
}
}
}
public override void PostCompile()
{
base.PostCompile();
// 检查 字段类型 与 所引用的表的key是否一致
foreach (var val in KeyValidators)
{
if (val is RefValidator refValidator)
{
var cfgTable = Assembly.GetCfgTable(refValidator.FirstTable);
if (CType is TMap mapType)
{
if (mapType.KeyType.GetType() != cfgTable.KeyTType.GetType())
{
throw new Exception($"type:{HostType.FullName} field:{Name} key类型:{mapType.KeyType.GetType()} 与 被引用的表:{cfgTable.FullName} key类型:{cfgTable.KeyTType.GetType()} 不一致");
}
}
else
{
throw new Exception($"type:{HostType.FullName} field:{Name} 不是 map类型. 不能指定 key_validator 引用");
}
}
}
var remainValidators = new List<IValidator>(this.Validators);
remainValidators.AddRange(this.ValueValidators);
foreach (var val in remainValidators)
{
if (val is RefValidator refValidator)
{
var cfgTable = Assembly.GetCfgTable(refValidator.FirstTable);
TType valueType;
switch (CType)
{
case TArray ta: valueType = ta.ElementType; break;
case TList tl: valueType = tl.ElementType; break;
case TSet ts: valueType = ts.ElementType; break;
case TMap tm: valueType = tm.ValueType; break;
default: valueType = CType; break;
}
if (valueType.GetType() != cfgTable.KeyTType.GetType())
{
throw new Exception($"type:{HostType.FullName} field:{Name} 类型:{valueType.GetType()} 与 被引用的表:{cfgTable.FullName} key类型:{cfgTable.KeyTType.GetType()} 不一致");
}
}
}
}
}
}

View File

@ -0,0 +1,149 @@
using Luban.Config.Common.RawDefs;
using Luban.Job.Common.Types;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Luban.Job.Cfg.Defs
{
public class DefTable : CfgDefTypeBase
{
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
public DefTable(Table b)
{
Name = b.Name;
Namespace = b.Namespace;
Index = b.Index;
ValueType = b.ValueType;
Mode = b.Mode;
InputFiles = b.InputFiles;
Groups = b.Groups;
}
public string Index { get; private set; }
public string ValueType { get; }
public ETableMode Mode { get; }
public bool IsMapTable => Mode == ETableMode.MAP;
public bool IsOneValueTable => Mode == ETableMode.ONE;
public bool IsTwoKeyMapTable => Mode == ETableMode.BMAP;
public List<string> InputFiles { get; }
public List<string> Groups { get; }
public TType KeyTType { get; private set; }
public TBean ValueTType { get; private set; }
public DefField IndexField { get; private set; }
public int IndexFieldIdIndex { get; private set; }
public string Index1 { get; private set; }
public TType KeyTType1 { get; private set; }
public DefField IndexField1 { get; private set; }
public int IndexFieldIdIndex1 { get; private set; }
public string Index2 { get; private set; }
public TType KeyTType2 { get; private set; }
public DefField IndexField2 { get; private set; }
public int IndexFieldIdIndex2 { get; private set; }
public bool NeedExport => Assembly.NeedExport(this.Groups);
public string OutputDataFile => $"{FullName}.bin";
public string JsonOutputDataFile => $"{FullName}.json";
public override void Compile()
{
var pass = Assembly;
if ((ValueTType = (TBean)pass.CreateType(Namespace, ValueType)) == null)
{
throw new Exception($"table:{FullName} 的 value类型:{ValueType} 不存在");
}
switch (Mode)
{
case ETableMode.ONE:
{
KeyTType = KeyTType2 = null;
break;
}
case ETableMode.MAP:
{
if (!string.IsNullOrWhiteSpace(Index))
{
if (ValueTType.GetBeanAs<DefBean>().TryGetField(Index, out var f, out var i))
{
IndexField = f;
IndexFieldIdIndex = i;
}
else
{
throw new Exception($"table:{FullName} index:{Index} 字段不存在");
}
}
else if (ValueTType.Bean.HierarchyFields.Count == 0)
{
throw new Exception($"table:{FullName} 必须定义至少一个字段");
}
else
{
IndexField = (DefField)ValueTType.Bean.HierarchyFields[0];
Index = IndexField.Name;
IndexFieldIdIndex = 0;
}
KeyTType = IndexField.CType;
break;
}
case ETableMode.BMAP:
{
string[] indexs = Index.Split(',').Where(k => !string.IsNullOrWhiteSpace(k)).ToArray();
if (indexs.Length != 2)
{
throw new Exception($"table:{FullName}是双键表,index 必须指定两个key");
}
{
Index1 = indexs[0];
if (ValueTType.GetBeanAs<DefBean>().TryGetField(Index1, out var f, out var i))
{
IndexField1 = f;
KeyTType = KeyTType1 = IndexField1.CType;
IndexFieldIdIndex1 = i;
}
else
{
throw new Exception($"table:{FullName} index:{Index} 字段不存在");
}
}
{
Index2 = indexs[1];
if (ValueTType.Bean.TryGetField(Index2, out var f, out var i))
{
IndexField2 = (DefField)f;
KeyTType2 = IndexField2.CType;
IndexFieldIdIndex2 = i;
}
else
{
throw new Exception($"table:{FullName} index:{indexs[1]} 字段不存在");
}
}
break;
}
default: throw new Exception($"unknown mode:{Mode}");
}
}
}
}

View File

@ -0,0 +1,156 @@
using Luban.Job.Cfg.TypeVisitors;
using Luban.Job.Common.Defs;
using Luban.Job.Common.Types;
using Luban.Job.Common.TypeVisitors;
namespace Luban.Job.Cfg.Defs
{
class TTypeTemplateExtends : TTypeTemplateCommonExtends
{
public static string CsDeserialize(string bufName, string fieldName, TType type)
{
return type.Apply(CsDeserializeVisitor.Ins, bufName, fieldName);
}
public static string CsCompatibleDeserialize(string bufName, string fieldName, TType type)
{
return type.Apply(CsDeserializeVisitor.Ins, bufName, fieldName);
}
public static string CsJsonDeserialize(string bufName, string fieldName, string jsonFieldName, TType type)
{
if (type.IsNullable)
{
return $"{{ var _j = {bufName}.GetProperty(\"{jsonFieldName}\"); if (_j.ValueKind != JsonValueKind.Null) {{ {type.Apply(CsJsonUserialize.Ins, "_j", fieldName)} }} else {{ {fieldName} = null; }} }}";
}
else
{
return type.Apply(CsJsonUserialize.Ins, $"{bufName}.GetProperty(\"{jsonFieldName}\")", fieldName);
}
}
public static string CsRecursiveResolve(DefField field, string tables)
{
return field.CType.Apply(CsRecursiveResolveVisitor.Ins, field.CsStyleName, tables);
}
public static string CsRefValidatorResolve(DefField field)
{
var refVarName = field.CsRefVarName;
var name = field.CsStyleName;
var tableName = field.Ref.FirstTable;
var table = field.Assembly.GetCfgTable(field.Ref.FirstTable);
if (field.IsNullable)
{
return $"this.{refVarName} = this.{name} != null ? (_tables[\"{tableName}\"] as {table.FullName}).GetOrDefault({name}.Value) : null;";
}
else
{
return $"this.{refVarName} = (_tables[\"{tableName}\"] as {table.FullName}).GetOrDefault({name});";
}
}
public static string JavaDeserialize(string bufName, string fieldName, TType type)
{
return type.Apply(JavaDeserializeVisitor.Ins, bufName, fieldName);
}
public static string JavaRecursiveResolve(DefField field, string tables)
{
return field.CType.Apply(JavaRecursiveResolveVisitor.Ins, field.JavaStyleName, tables);
}
public static string JavaRefValidatorResolve(DefField field)
{
var refVarName = field.JavaRefVarName;
var name = field.JavaStyleName;
var tableName = field.Ref.FirstTable;
var table = field.Assembly.GetCfgTable(field.Ref.FirstTable);
if (field.IsNullable)
{
return $"this.{refVarName} = this.{name} != null ? (({table.FullNameWithTopModule})_tables.get(\"{tableName}\")).get({name}.Value) : null;";
}
else
{
return $"this.{refVarName} = (({table.FullNameWithTopModule})_tables.get(\"{tableName}\")).get({name});";
}
}
public static string CppDeserialize(string bufName, string fieldName, TType type)
{
return type.Apply(CppDeserializeVisitor.Ins, bufName, fieldName);
}
public static string LuaCommentType(TType type)
{
return type.Apply(LuaCommentTypeVisitor.Ins);
}
public static string LuaUnderingDeserialize(string bufName, TType type)
{
return type.Apply(LuaUnderingDeserializeVisitor.Ins, bufName);
}
public static string GoDefineType(TType type)
{
return type.Apply(GoTypeNameVisitor.Ins);
}
public static string GoDeserializeType(TType type, string bufName)
{
return type.Apply(GoDeserializeVisitor.Ins, bufName);
}
public static string GoDeserializeField(DefField field, string bufName)
{
var name = field.CsStyleName;
TType type = field.CType;
if (field.NeedMarshalBoolPrefix)
{
return $"{{ var _exists bool; if _exists, err = {bufName}.ReadBool(); err != nil {{ return }}; if _exists {{ if _v.{name}, err = {type.Apply(GoDeserializeVisitor.Ins, bufName)}; err != nil {{ return }} }} }}";
}
else
{
return $"if _v.{name}, err = {type.Apply(GoDeserializeVisitor.Ins, bufName)}; err != nil {{ return }} ";
}
}
public static string TsDeserialize(string fieldName, string jsonFieldName, TType type)
{
return type.Apply(TsDeserializeVisitor.Ins, $"{jsonFieldName}", fieldName);
}
public static string TsRecursiveResolve(DefField field, string tables)
{
return field.CType.Apply(TsRecursiveResolveVisitor.Ins, "this." + field.CsStyleName, tables);
}
public static string TsRefValidatorResolve(DefField field)
{
var refVarName = field.CsRefVarName;
var name = "this." + field.TsStyleName;
var tableName = field.Ref.FirstTable;
var table = field.Assembly.GetCfgTable(field.Ref.FirstTable);
if (field.IsNullable)
{
return $"this.{refVarName} = {name} != null ? (_tables.get('{tableName}') as {table.FullName}).get({name}) : null;";
}
else
{
return $"this.{refVarName} = (_tables.get('{tableName}') as {table.FullName}).get({name});";
}
}
public static string Py3Deserialize(string fieldName, string jsonFieldName, TType type)
{
return type.Apply(PyDeserializeVisitor.Py3Ins, $"{jsonFieldName}", fieldName);
}
public static string Py27Deserialize(string fieldName, string jsonFieldName, TType type)
{
return type.Apply(PyDeserializeVisitor.Py27Ins, $"{jsonFieldName}", fieldName);
}
}
}

View File

@ -0,0 +1,28 @@
using Luban.Job.Cfg.Defs;
using Luban.Job.Common.Defs;
using System;
using System.Collections.Generic;
namespace Luban.Job.Cfg.Generate
{
public abstract class CodeRenderBase : ICodeRender
{
public abstract string Render(DefConst c);
public abstract string Render(DefEnum c);
public abstract string Render(DefBean b);
public abstract string Render(DefTable c);
public abstract string RenderService(string name, string module, List<DefTable> tables);
public string RenderAny(object o)
{
switch (o)
{
case DefConst c: return Render(c);
case DefEnum e: return Render(e);
case DefBean b: return Render(b);
case DefTable r: return Render(r);
default: throw new Exception($"unknown render type:{o}");
}
}
}
}

View File

@ -0,0 +1,304 @@
using Luban.Job.Cfg.Defs;
using Luban.Job.Common.Defs;
using Luban.Job.Common.Utils;
using Scriban;
using System;
using System.Collections.Generic;
namespace Luban.Job.Cfg.Generate
{
class CppBinCodeRender : CodeRenderBase
{
public override string Render(DefConst c)
{
return RenderUtil.RenderCppConstClass(c);
}
public override string Render(DefEnum c)
{
return RenderUtil.RenderCppEnumClass(c);
}
public string RenderForwardDefine(DefBean b)
{
return $"{b.CppNamespaceBegin} class {b.Name}; {b.CppNamespaceEnd} ";
}
[ThreadStatic]
private static Template t_beanRender;
public override string Render(DefBean b)
{
var template = t_beanRender ??= Template.Parse(@"
{{x.cpp_namespace_begin}}
{{
name = x.name
parent_def_type = x.parent_def_type
export_fields = x.export_fields
hierarchy_export_fields = x.hierarchy_export_fields
}}
class {{name}} : public {{if parent_def_type}} {{parent_def_type.cpp_full_name}} {{else}} bright::CfgBean {{end}}
{
public:
static bool deserialize{{name}}(ByteBuf& _buf, {{name}}*& _out);
{{name}}()
{
}
{{name}}({{- for field in hierarchy_export_fields }}{{cpp_define_type field.ctype}} {{field.name}}{{if !for.last}},{{end}} {{end}})
{{-if parent_def_type-}}
: {{parent_def_type.cpp_full_name}}({{ for field in parent_def_type.hierarchy_export_fields }}{{field.name}}{{if !for.last}}, {{end}}{{end}})
{{-end-}}
{
{{~ for field in export_fields ~}}
this->{{field.cpp_style_name}} = {{field.name}};
{{~end~}}
}
virtual ~{{name}}() {}
bool deserialize(ByteBuf& _buf);
{{~ for field in export_fields ~}}
{{cpp_define_type field.ctype}} {{field.cpp_style_name}};
{{~end~}}
{{if !x.is_abstract_type}}
static constexpr int ID = {{x.id}};
int getTypeId() const { return ID; }
{{end}}
};
{{x.cpp_namespace_end}}
");
var result = template.RenderCode(b);
return result;
}
[ThreadStatic]
private static Template t_tableRender;
public override string Render(DefTable p)
{
var template = t_tableRender ??= Template.Parse(@"
{{x.cpp_namespace_begin}}
{{~
name = x.name
key_type = x.key_ttype
key_type1 = x.key_ttype1
key_type2 = x.key_ttype2
value_type = x.value_ttype
~}}
class {{name}}
{
{{~ if x.is_two_key_map_table ~}}
private:
std::unordered_map<{{cpp_define_type key_type1}}, std::vector<{{cpp_define_type value_type}}>> _dataListMap;
std::unordered_map<{{cpp_define_type key_type1}}, std::unordered_map<{{cpp_define_type key_type2}}, {{cpp_define_type value_type}}>> _dataMapMap;
std::vector<{{cpp_define_type value_type}}> _dataList;
public:
bool load(ByteBuf& _buf)
{
int n;
if (!_buf.readSize(n)) return false;
for(; n > 0 ; --n)
{
{{cpp_define_type value_type}} _v;
{{cpp_deserialize '_buf' '_v' value_type}}
_dataList.push_back(_v);
auto _key = _v->{{x.index_field1.cpp_style_name}};
auto& list = _dataListMap[_key];
list.push_back(_v);
auto& map = _dataMapMap[_key];
map[_v->{{x.index_field2.cpp_style_name}}] = _v;
}
return true;
}
const std::unordered_map<{{cpp_define_type key_type1}},std::vector<{{cpp_define_type value_type}}>>& getDataListMap() const { return _dataListMap; }
const std::unordered_map<{{cpp_define_type key_type1}}, std::unordered_map<{{cpp_define_type key_type2}}, {{cpp_define_type value_type}}>>& getDataMapMap() const {return _dataMapMap;}
const std::vector<{{cpp_define_type value_type}}>& getDataList() const { return _dataList; }
const {{cpp_define_type value_type}} get({{cpp_define_type key_type1}} key1, {{cpp_define_type key_type2}} key2) const
{
auto it1 = _dataMapMap.find(key1);
if (it1 == _dataMapMap.end()) { return nullptr; }
auto it2 = it1->second.find(key2);
return it2 != it1->second.end() ? it2->second : nullptr;
}
{{~else if x.is_map_table ~}}
private:
std::unordered_map<{{cpp_define_type key_type}}, {{cpp_define_type value_type}}> _dataMap;
std::vector<{{cpp_define_type value_type}}> _dataList;
public:
bool load(ByteBuf& _buf)
{
int n;
if (!_buf.readSize(n)) return false;
for(; n > 0 ; --n)
{
{{cpp_define_type value_type}} _v;
{{cpp_deserialize '_buf' '_v' value_type}}
_dataList.push_back(_v);
_dataMap[_v->{{x.index_field.cpp_style_name}}] = _v;
}
return true;
}
const std::unordered_map<{{cpp_define_type key_type}}, {{cpp_define_type value_type}}>& getDataMap() const { return _dataMap; }
const std::vector<{{cpp_define_type value_type}}>& getDataList() const { return _dataList; }
const {{cpp_define_type value_type}} get({{cpp_define_type key_type}} key)
{
auto it = _dataMap.find(key);
return it != _dataMap.end() ? it->second : nullptr;
}
{{~else~}}
private:
{{cpp_define_type value_type}} _data;
public:
const {{cpp_define_type value_type}} data() const { return _data; }
bool load(ByteBuf& _buf)
{
int n;
if (!_buf.readSize(n)) return false;
if (n != 1) return false;
{{cpp_deserialize '_buf' '_data' value_type}}
return true;
}
{{~ for field in value_type.bean.hierarchy_export_fields ~}}
{{cpp_define_type field.ctype}}& {{field.cpp_getter_name}}() const { return _data->{{field.cpp_style_name}}; }
{{~end~}}
{{end}}
};
{{x.cpp_namespace_end}}
");
var result = template.RenderCode(p);
return result;
}
[ThreadStatic]
private static Template t_serviceRender;
public override string RenderService(string name, string module, List<DefTable> tables)
{
var template = t_serviceRender ??= Template.Parse(@"
class {{name}}
{
public:
{{- for table in tables }}
{{table.cpp_full_name}} {{table.name}};
{{-end}}
bool load(std::function<bool(ByteBuf&, const std::string&)> loader)
{
ByteBuf buf;
{{- for table in tables }}
if (!loader(buf, ""{{table.output_data_file}}"")) return false;
if (!{{table.name}}.load(buf)) return false;
{{-end}}
return true;
}
};
");
var result = template.Render(new
{
Name = name,
Tables = tables,
});
return result;
}
[ThreadStatic]
private static Template t_stubRender;
public string RenderStub(string topModule, List<DefTypeBase> types)
{
var template = t_stubRender ??= Template.Parse(@"
#include <algorithm>
#include ""gen_types.h""
using ByteBuf = bright::serialization::ByteBuf;
namespace {{x.top_module}}
{
{{~for type in x.types~}}
bool {{type.cpp_full_name}}::deserialize(ByteBuf& _buf)
{
{{~if type.parent_def_type~}}
if (!{{type.parent_def_type.cpp_full_name}}::deserialize(_buf))
{
return false;
}
{{~end~}}
{{~ for field in type.export_fields ~}}
{{cpp_deserialize '_buf' field.cpp_style_name field.ctype}}
{{~end~}}
return true;
}
bool {{type.cpp_full_name}}::deserialize{{type.name}}(ByteBuf& _buf, {{type.cpp_full_name}}*& _out)
{
{{if type.is_abstract_type}}
int id;
if (!_buf.readInt(id)) return false;
switch (id)
{
case 0 : { _out = nullptr; return true; }
{{- for child in type.hierarchy_not_abstract_children}}
case {{child.cpp_full_name}}::ID: { _out = new {{child.cpp_full_name}}(); if (_out->deserialize(_buf)) { return true; } else { delete _out; _out = nullptr; return false;} }
{{-end}}
default: { _out = nullptr; return false;}
}
{{else}}
_out = new {{type.cpp_full_name}}();
if (_out->deserialize(_buf))
{
return true;
}
else
{
delete _out;
_out = nullptr;
return false;
}
{{end}}
}
{{~end~}}
}
");
return template.RenderCode(new
{
TopModule = topModule,
Types = types,
});
}
}
}

View File

@ -0,0 +1,315 @@
using Luban.Job.Cfg.Defs;
using Scriban;
using System;
using System.Collections.Generic;
namespace Luban.Job.Cfg.Generate
{
class CsBinCodeRender : CsCodeRenderBase
{
[ThreadStatic]
private static Template t_beanRender;
public override string Render(DefBean b)
{
var template = t_beanRender ??= Template.Parse(@"
using Bright.Serialization;
using System.Collections.Generic;
{{
name = x.name
parent_def_type = x.parent_def_type
export_fields = x.export_fields
hierarchy_export_fields = x.hierarchy_export_fields
}}
namespace {{x.namespace_with_top_module}}
{
public {{x.cs_class_modifier}} partial class {{name}} : {{if parent_def_type}} {{x.parent}} {{else}} Bright.Config.BeanBase {{end}}
{
public {{name}}(ByteBuf _buf) {{if parent_def_type}} : base(_buf) {{end}}
{
{{~ for field in export_fields ~}}
{{cs_deserialize '_buf' field.cs_style_name field.ctype}}
{{~if field.index_field~}}
foreach(var _v in {{field.cs_style_name}})
{
{{field.cs_style_name}}_Index.Add(_v.{{field.index_field.cs_style_name}}, _v);
}
{{~end~}}
{{~end~}}
}
public {{name}}({{- for field in hierarchy_export_fields }}{{cs_define_type field.ctype}} {{field.name}}{{if !for.last}},{{end}} {{end}}) {{if parent_def_type}} : base({{- for field in parent_def_type.hierarchy_export_fields }}{{field.name}}{{if !for.last}},{{end}}{{end}}) {{end}}
{
{{~ for field in export_fields ~}}
this.{{field.cs_style_name}} = {{field.name}};
{{~if field.index_field~}}
foreach(var _v in {{field.cs_style_name}})
{
{{field.cs_style_name}}_Index.Add(_v.{{field.index_field.cs_style_name}}, _v);
}
{{~end~}}
{{~end~}}
}
public static {{name}} Deserialize{{name}}(ByteBuf _buf)
{
{{if x.is_abstract_type}}
switch (_buf.ReadInt())
{
case 0 : return null;
{{- for child in x.hierarchy_not_abstract_children}}
case {{child.full_name}}.ID: return new {{child.full_name}}(_buf);
{{-end}}
default: throw new SerializationException();
}
{{else}}
return new {{x.full_name}}(_buf);
{{end}}
}
{{~ for field in export_fields ~}}
public readonly {{cs_define_type field.ctype}} {{field.cs_style_name}};
{{~if field.index_field~}}
public readonly Dictionary<{{cs_define_type field.index_field.ctype}}, {{cs_define_type field.ctype.element_type}}> {{field.cs_style_name}}_Index = new Dictionary<{{cs_define_type field.index_field.ctype}}, {{cs_define_type field.ctype.element_type}}>();
{{~end~}}
{{~if field.gen_ref~}}
public {{field.cs_ref_validator_define}}
{{~end~}}
{{~end~}}
{{if !x.is_abstract_type}}
public const int ID = {{x.id}};
public override int GetTypeId() => ID;
{{end}}
public {{x.cs_method_modifier}} void Resolve(Dictionary<string, object> _tables)
{
{{~if parent_def_type}}base.Resolve(_tables);{{end}}
{{~ for field in export_fields ~}}
{{~if field.gen_ref~}}
{{cs_ref_validator_resolve field}}
{{~else if field.has_recursive_ref~}}
{{cs_recursive_resolve field '_tables'}}
{{~end~}}
{{~end~}}
OnResolveFinish(_tables);
}
partial void OnResolveFinish(Dictionary<string, object> _tables);
public override string ToString()
{
return ""{{full_name}}{ ""
{{- for field in hierarchy_export_fields }}
+ ""{{field.cs_style_name}}:"" + {{cs_to_string field.cs_style_name field.ctype}} + "",""
{{-end}}
+ ""}"";
}
}
}
");
var result = template.RenderCode(b);
return result;
}
[ThreadStatic]
private static Template t_tableRender;
public override string Render(DefTable p)
{
var template = t_tableRender ??= Template.Parse(@"
using Bright.Serialization;
using System.Collections.Generic;
namespace {{x.namespace_with_top_module}}
{
{{
name = x.name
key_type = x.key_ttype
key_type1 = x.key_ttype1
key_type2 = x.key_ttype2
value_type = x.value_ttype
}}
public sealed partial class {{name}}
{
{{~ if x.is_two_key_map_table ~}}
private readonly Dictionary<{{cs_define_type key_type1}}, List<{{cs_define_type value_type}}>> _dataListMap;
private readonly Dictionary<{{cs_define_type key_type1}}, Dictionary<{{cs_define_type key_type2}}, {{cs_define_type value_type}}>> _dataMapMap;
private readonly List<{{cs_define_type value_type}}> _dataList;
public {{name}}(ByteBuf _buf)
{
_dataListMap = new Dictionary<{{cs_define_type key_type1}}, List<{{cs_define_type value_type}}>>();
_dataMapMap = new Dictionary<{{cs_define_type key_type1}}, Dictionary<{{cs_define_type key_type2}}, {{cs_define_type value_type}}>>();
_dataList = new List<{{cs_define_type value_type}}>();
for(int n = _buf.ReadSize() ; n > 0 ; --n)
{
{{cs_define_type value_type}} _v;
{{cs_deserialize '_buf' '_v' value_type}}
_dataList.Add(_v);
var _key = _v.{{x.index_field1.cs_style_name}};
if (!_dataListMap.TryGetValue(_key, out var list))
{
list = new List<{{cs_define_type value_type}}>();
_dataListMap.Add(_key, list);
}
list.Add(_v);
if (!_dataMapMap.TryGetValue(_key, out var map))
{
map = new Dictionary<{{cs_define_type key_type2}}, {{cs_define_type value_type}}>();
_dataMapMap.Add(_key, map);
}
map.Add(_v.{{x.index_field2.cs_style_name}}, _v);
}
}
public Dictionary<{{cs_define_type key_type1}}, List<{{cs_define_type value_type}}>> DataListMap => _dataListMap;
public Dictionary<{{cs_define_type key_type1}}, Dictionary<{{cs_define_type key_type2}}, {{cs_define_type value_type}}>> DataMapMap => _dataMapMap;
public List<{{cs_define_type value_type}}> DataList => _dataList;
{{if value_type.is_dynamic}}
public T GetOrDefaultAs<T>({{cs_define_type key_type1}} key1, {{cs_define_type key_type2}} key2) where T : {{cs_define_type value_type}} => _dataMapMap.TryGetValue(key1, out var m) && m.TryGetValue(key2, out var v) ? (T)v : null;
public T GetAs<T>({{cs_define_type key_type1}} key1, {{cs_define_type key_type2}} key2) where T : {{cs_define_type value_type}} => (T)_dataMapMap[key1][key2];
{{end}}
public {{cs_define_type value_type}} GetOrDefault({{cs_define_type key_type1}} key1, {{cs_define_type key_type2}} key2) => _dataMapMap.TryGetValue(key1, out var m) && m.TryGetValue(key2, out var v) ? v : null;
public {{cs_define_type value_type}} Get({{cs_define_type key_type1}} key1, {{cs_define_type key_type2}} key2) => _dataMapMap[key1][key2];
public {{cs_define_type value_type}} this[{{cs_define_type key_type1}} key1, {{cs_define_type key_type2}} key2] => _dataMapMap[key1][key2];
public void Resolve(Dictionary<string, object> _tables)
{
foreach(var v in _dataList)
{
v.Resolve(_tables);
}
OnResolveFinish(_tables);
}
{{~else if x.is_map_table ~}}
private readonly Dictionary<{{cs_define_type key_type}}, {{cs_define_type value_type}}> _dataMap;
private readonly List<{{cs_define_type value_type}}> _dataList;
public {{name}}(ByteBuf _buf)
{
_dataMap = new Dictionary<{{cs_define_type key_type}}, {{cs_define_type value_type}}>();
_dataList = new List<{{cs_define_type value_type}}>();
for(int n = _buf.ReadSize() ; n > 0 ; --n)
{
{{cs_define_type value_type}} _v;
{{cs_deserialize '_buf' '_v' value_type}}
_dataList.Add(_v);
_dataMap.Add(_v.{{x.index_field.cs_style_name}}, _v);
}
}
public Dictionary<{{cs_define_type key_type}}, {{cs_define_type value_type}}> DataMap => _dataMap;
public List<{{cs_define_type value_type}}> DataList => _dataList;
{{~if value_type.is_dynamic~}}
public T GetOrDefaultAs<T>({{cs_define_type key_type}} key) where T : {{cs_define_type value_type}} => _dataMap.TryGetValue(key, out var v) ? (T)v : null;
public T GetAs<T>({{cs_define_type key_type}} key) where T : {{cs_define_type value_type}} => (T)_dataMap[key];
{{~end~}}
public {{cs_define_type value_type}} GetOrDefault({{cs_define_type key_type}} key) => _dataMap.TryGetValue(key, out var v) ? v : null;
public {{cs_define_type value_type}} Get({{cs_define_type key_type}} key) => _dataMap[key];
public {{cs_define_type value_type}} this[{{cs_define_type key_type}} key] => _dataMap[key];
public void Resolve(Dictionary<string, object> _tables)
{
foreach(var v in _dataList)
{
v.Resolve(_tables);
}
OnResolveFinish(_tables);
}
{{~else~}}
private readonly {{cs_define_type value_type}} _data;
public {{name}}(ByteBuf _buf)
{
int n = _buf.ReadSize();
if (n != 1) throw new SerializationException(""table mode=one, but size != 1"");
{{cs_deserialize '_buf' '_data' value_type}}
}
{{~ for field in value_type.bean.hierarchy_export_fields ~}}
public {{cs_define_type field.ctype}} {{field.cs_style_name}} => _data.{{field.cs_style_name}};
{{~if field.ref~}}
public {{field.cs_ref_type_name}} {{field.cs_ref_var_name}} => _data.{{field.cs_ref_var_name}};
{{~end~}}
{{~end~}}
public void Resolve(Dictionary<string, object> _tables)
{
_data.Resolve(_tables);
OnResolveFinish(_tables);
}
{{end}}
partial void OnResolveFinish(Dictionary<string, object> _tables);
}
}
");
var result = template.RenderCode(p);
return result;
}
[ThreadStatic]
private static Template t_serviceRender;
public override string RenderService(string name, string module, List<DefTable> tables)
{
var template = t_serviceRender ??= Template.Parse(@"
using Bright.Serialization;
{{
name = x.name
namespace = x.namespace
tables = x.tables
}}
namespace {{namespace}}
{
public sealed class {{name}}
{
{{- for table in tables }}
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}}
}
}
}
");
var result = template.RenderCode(new
{
Name = name,
Namespace = module,
Tables = tables,
});
return result;
}
}
}

View File

@ -0,0 +1,18 @@
using Luban.Job.Common.Defs;
using Luban.Job.Common.Utils;
namespace Luban.Job.Cfg.Generate
{
public abstract class CsCodeRenderBase : CodeRenderBase
{
public override string Render(DefConst c)
{
return RenderUtil.RenderCsConstClass(c);
}
public override string Render(DefEnum e)
{
return RenderUtil.RenderCsEnumClass(e);
}
}
}

View File

@ -0,0 +1,309 @@
using Luban.Job.Cfg.Defs;
using Scriban;
using System;
using System.Collections.Generic;
namespace Luban.Job.Cfg.Generate
{
class CsJsonCodeRender : CsCodeRenderBase
{
[ThreadStatic]
private static Template t_beanRender;
public override string Render(DefBean b)
{
var template = t_beanRender ??= Template.Parse(@"
using Bright.Serialization;
using System.Collections.Generic;
using System.Text.Json;
{{
name = x.name
parent_def_type = x.parent_def_type
parent = x.parent
export_fields = x.export_fields
hierarchy_export_fields = x.hierarchy_export_fields
}}
namespace {{x.namespace_with_top_module}}
{
public {{x.cs_class_modifier}} partial class {{name}} : {{if parent_def_type}} {{parent}} {{else}} Bright.Config.BeanBase {{end}}
{
public {{name}}(JsonElement _buf) {{if parent_def_type}} : base(_buf) {{end}}
{
{{~ for field in export_fields ~}}
{{cs_json_deserialize '_buf' field.cs_style_name field.name field.ctype}}
{{~if field.index_field~}}
foreach(var _v in {{field.cs_style_name}}) { {{field.cs_style_name}}_Index.Add(_v.{{field.index_field.cs_style_name}}, _v); }
{{~end~}}
{{~end~}}
}
public {{name}}({{- for field in hierarchy_export_fields }}{{cs_define_type field.ctype}} {{field.name}}{{if !for.last}},{{end}} {{end}}) {{if parent_def_type}} : base({{- for field in parent_def_type.hierarchy_export_fields }}{{field.name}}{{if !for.last}},{{end}}{{end}}) {{end}}
{
{{- for field in export_fields }}
this.{{field.cs_style_name}} = {{field.name}};
{{-if field.index_field}}
foreach(var _v in {{field.cs_style_name}}) { {{field.cs_style_name}}_Index.Add(_v.{{field.index_field.cs_style_name}}, _v); }
{{-end}}
{{-end}}
}
public static {{name}} Deserialize{{name}}(JsonElement _buf)
{
{{if x.is_abstract_type}}
if (_buf.ValueKind == JsonValueKind.Null) { return null; }
switch (_buf.GetProperty(""__type__"").GetString())
{
{{- for child in x.hierarchy_not_abstract_children}}
case ""{{child.name}}"": return new {{child.full_name}}(_buf);
{{-end}}
default: throw new SerializationException();
}
{{else}}
return new {{x.full_name}}(_buf);
{{end}}
}
{{~ for field in export_fields ~}}
public readonly {{cs_define_type field.ctype}} {{field.cs_style_name}};
{{~if field.index_field~}}
public readonly Dictionary<{{cs_define_type field.index_field.ctype}}, {{cs_define_type field.ctype.element_type}}> {{field.cs_style_name}}_Index = new Dictionary<{{cs_define_type field.index_field.ctype}}, {{cs_define_type field.ctype.element_type}}>();
{{~end~}}
{{~if field.gen_ref~}}
public {{field.cs_ref_validator_define}}
{{~end~}}
{{~end~}}
{{if !x.is_abstract_type}}
public const int ID = {{x.id}};
public override int GetTypeId() => ID;
{{end}}
public {{x.cs_method_modifier}} void Resolve(Dictionary<string, object> _tables)
{
{{~if parent_def_type}}base.Resolve(_tables);{{end}}
{{~ for field in export_fields ~}}
{{~if field.gen_ref~}}
{{cs_ref_validator_resolve field}}
{{~else if field.has_recursive_ref~}}
{{cs_recursive_resolve field '_tables'}}
{{~end~}}
{{~end~}}
OnResolveFinish(_tables);
}
partial void OnResolveFinish(Dictionary<string, object> _tables);
public override string ToString()
{
return ""{{full_name}}{ ""
{{- for field in hierarchy_export_fields }}
+ ""{{field.cs_style_name}}:"" + {{cs_to_string field.cs_style_name field.ctype}} + "",""
{{-end}}
+ ""}"";
}
}
}
");
var result = template.RenderCode(b);
return result;
}
[ThreadStatic]
private static Template t_tableRender;
public override string Render(DefTable p)
{
var template = t_tableRender ??= Template.Parse(@"
using Bright.Serialization;
using System.Collections.Generic;
using System.Text.Json;
{{
name = x.name
key_type = x.key_ttype
key_type1 = x.key_ttype1
key_type2 = x.key_ttype2
value_type = x.value_ttype
}}
namespace {{x.namespace_with_top_module}}
{
public sealed partial class {{name}}
{
{{~ if x.is_two_key_map_table ~}}
private readonly Dictionary<{{cs_define_type key_type1}}, List<{{cs_define_type value_type}}>> _dataListMap;
private readonly Dictionary<{{cs_define_type key_type1}}, Dictionary<{{cs_define_type key_type2}}, {{cs_define_type value_type}}>> _dataMapMap;
private readonly List<{{cs_define_type value_type}}> _dataList;
public {{name}}(JsonElement _buf)
{
_dataListMap = new Dictionary<{{cs_define_type key_type1}}, List<{{cs_define_type value_type}}>>();
_dataMapMap = new Dictionary<{{cs_define_type key_type1}}, Dictionary<{{cs_define_type key_type2}}, {{cs_define_type value_type}}>>();
_dataList = new List<{{cs_define_type value_type}}>();
foreach(JsonElement _row in _buf.EnumerateArray())
{
var _v = {{cs_define_type value_type}}.Deserialize{{value_type.bean.name}}(_row);
_dataList.Add(_v);
var _key = _v.{{x.index_field1.cs_style_name}};
if (!_dataListMap.TryGetValue(_key, out var list))
{
list = new List<{{cs_define_type value_type}}>();
_dataListMap.Add(_key, list);
}
list.Add(_v);
if (!_dataMapMap.TryGetValue(_key, out var map))
{
map = new Dictionary<{{cs_define_type key_type2}}, {{cs_define_type value_type}}>();
_dataMapMap.Add(_key, map);
}
map.Add(_v.{{x.index_field2.cs_style_name}}, _v);
}
}
public Dictionary<{{cs_define_type key_type1}}, List<{{cs_define_type value_type}}>> DataListMap => _dataListMap;
public Dictionary<{{cs_define_type key_type1}}, Dictionary<{{cs_define_type key_type2}}, {{cs_define_type value_type}}>> DataMapMap => _dataMapMap;
public List<{{cs_define_type value_type}}> DataList => _dataList;
{{if value_type.is_dynamic}}
public T GetOrDefaultAs<T>({{cs_define_type key_type1}} key1, {{cs_define_type key_type2}} key2) where T : {{cs_define_type value_type}} => _dataMapMap.TryGetValue(key1, out var m) && m.TryGetValue(key2, out var v) ? (T)v : null;
public T GetAs<T>({{cs_define_type key_type1}} key1, {{cs_define_type key_type2}} key2) where T : {{cs_define_type value_type}} => (T)_dataMapMap[key1][key2];
{{end}}
public {{cs_define_type value_type}} GetOrDefault({{cs_define_type key_type1}} key1, {{cs_define_type key_type2}} key2) => _dataMapMap.TryGetValue(key1, out var m) && m.TryGetValue(key2, out var v) ? v : null;
public {{cs_define_type value_type}} Get({{cs_define_type key_type1}} key1, {{cs_define_type key_type2}} key2) => _dataMapMap[key1][key2];
public {{cs_define_type value_type}} this[{{cs_define_type key_type1}} key1, {{cs_define_type key_type2}} key2] => _dataMapMap[key1][key2];
public void Resolve(Dictionary<string, object> _tables)
{
foreach(var v in _dataList)
{
v.Resolve(_tables);
}
OnResolveFinish(_tables);
}
{{~else if x.is_map_table ~}}
private readonly Dictionary<{{cs_define_type key_type}}, {{cs_define_type value_type}}> _dataMap;
private readonly List<{{cs_define_type value_type}}> _dataList;
public {{name}}(JsonElement _buf)
{
_dataMap = new Dictionary<{{cs_define_type key_type}}, {{cs_define_type value_type}}>();
_dataList = new List<{{cs_define_type value_type}}>();
foreach(JsonElement _row in _buf.EnumerateArray())
{
var _v = {{cs_define_type value_type}}.Deserialize{{value_type.bean.name}}(_row);
_dataList.Add(_v);
_dataMap.Add(_v.{{x.index_field.cs_style_name}}, _v);
}
}
public Dictionary<{{cs_define_type key_type}}, {{cs_define_type value_type}}> DataMap => _dataMap;
public List<{{cs_define_type value_type}}> DataList => _dataList;
{{~if value_type.is_dynamic~}}
public T GetOrDefaultAs<T>({{cs_define_type key_type}} key) where T : {{cs_define_type value_type}} => _dataMap.TryGetValue(key, out var v) ? (T)v : null;
public T GetAs<T>({{cs_define_type key_type}} key) where T : {{cs_define_type value_type}} => (T)_dataMap[key];
{{~end~}}
public {{cs_define_type value_type}} GetOrDefault({{cs_define_type key_type}} key) => _dataMap.TryGetValue(key, out var v) ? v : null;
public {{cs_define_type value_type}} Get({{cs_define_type key_type}} key) => _dataMap[key];
public {{cs_define_type value_type}} this[{{cs_define_type key_type}} key] => _dataMap[key];
public void Resolve(Dictionary<string, object> _tables)
{
foreach(var v in _dataList)
{
v.Resolve(_tables);
}
OnResolveFinish(_tables);
}
{{~else~}}
private readonly {{cs_define_type value_type}} _data;
public {{name}}(JsonElement _buf)
{
int n = _buf.GetArrayLength();
if (n != 1) throw new SerializationException(""table mode=one, but size != 1"");
_data = {{cs_define_type value_type}}.Deserialize{{value_type.bean.name}}(_buf[0]);
}
{{~ for field in value_type.bean.hierarchy_export_fields ~}}
public {{cs_define_type field.ctype}} {{field.cs_style_name}} => _data.{{field.cs_style_name}};
{{~if field.ref~}}
public {{field.cs_ref_type_name}} {{field.cs_ref_var_name}} => _data.{{field.cs_ref_var_name}};
{{~end~}}
{{~end~}}
public void Resolve(Dictionary<string, object> _tables)
{
_data.Resolve(_tables);
OnResolveFinish(_tables);
}
{{end}}
partial void OnResolveFinish(Dictionary<string, object> _tables);
}
}
");
var result = template.RenderCode(p);
return result;
}
[ThreadStatic]
private static Template t_serviceRender;
public override string RenderService(string name, string module, List<DefTable> tables)
{
var template = t_serviceRender ??= Template.Parse(@"
using Bright.Serialization;
using System.Text.Json;
{{
name = x.name
namespace = x.namespace
tables = x.tables
}}
namespace {{namespace}}
{
public sealed partial class {{name}}
{
{{- for table in tables }}
public {{table.full_name}} {{table.name}} {get; }
{{-end}}
public {{name}}(System.Func<string, JsonElement> loader)
{
var tables = new System.Collections.Generic.Dictionary<string, object>();
{{- for table in tables }}
{{table.name}} = new {{table.full_name}}(loader(""{{table.json_output_data_file}}""));
tables.Add(""{{table.full_name}}"", {{table.name}});
{{-end}}
{{- for table in tables }}
{{table.name}}.Resolve(tables);
{{-end}}
}
}
}
");
var result = template.RenderCode(new
{
Name = name,
Namespace = module,
Tables = tables,
});
return result;
}
}
}

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