diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6d49f9c --- /dev/null +++ b/.gitignore @@ -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 + diff --git a/README.en-us.md b/README.en-us.md new file mode 100644 index 0000000..5a72d76 --- /dev/null +++ b/README.en-us.md @@ -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. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..caf5654 --- /dev/null +++ b/README.md @@ -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. \ No newline at end of file diff --git a/config/Datas/test/full_type.xlsx b/config/Datas/test/full_type.xlsx new file mode 100644 index 0000000..eba555b Binary files /dev/null and b/config/Datas/test/full_type.xlsx differ diff --git a/config/Datas/test/json_datas/tb_role.json b/config/Datas/test/json_datas/tb_role.json new file mode 100644 index 0000000..faa901b --- /dev/null +++ b/config/Datas/test/json_datas/tb_role.json @@ -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}] + } \ No newline at end of file diff --git a/config/Datas/test/lua_datas/demo.lua b/config/Datas/test/lua_datas/demo.lua new file mode 100644 index 0000000..5d8daf3 --- /dev/null +++ b/config/Datas/test/lua_datas/demo.lua @@ -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}}, +} \ No newline at end of file diff --git a/config/Datas/test/multi_level_title.xlsx b/config/Datas/test/multi_level_title.xlsx new file mode 100644 index 0000000..612735c Binary files /dev/null and b/config/Datas/test/multi_level_title.xlsx differ diff --git a/config/Datas/test/multi_rows_record.xlsx b/config/Datas/test/multi_rows_record.xlsx new file mode 100644 index 0000000..bc0fa38 Binary files /dev/null and b/config/Datas/test/multi_rows_record.xlsx differ diff --git a/config/Datas/test/table_one.xlsx b/config/Datas/test/table_one.xlsx new file mode 100644 index 0000000..2fa9c5a Binary files /dev/null and b/config/Datas/test/table_one.xlsx differ diff --git a/config/Datas/test/tb_role_csv.csv b/config/Datas/test/tb_role_csv.csv new file mode 100644 index 0000000..00465d9 --- /dev/null +++ b/config/Datas/test/tb_role_csv.csv @@ -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" +,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,, diff --git a/config/Datas/test/tb_role_one_xlsx.xlsx b/config/Datas/test/tb_role_one_xlsx.xlsx new file mode 100644 index 0000000..656fb05 Binary files /dev/null and b/config/Datas/test/tb_role_one_xlsx.xlsx differ diff --git a/config/Datas/test/tb_role_xlsx.xlsx b/config/Datas/test/tb_role_xlsx.xlsx new file mode 100644 index 0000000..40578d5 Binary files /dev/null and b/config/Datas/test/tb_role_xlsx.xlsx differ diff --git a/config/Datas/test/tbrole_datas/tb_role.json b/config/Datas/test/tbrole_datas/tb_role.json new file mode 100644 index 0000000..681057d --- /dev/null +++ b/config/Datas/test/tbrole_datas/tb_role.json @@ -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} + } \ No newline at end of file diff --git a/config/Datas/test/xml_datas/demo.xml b/config/Datas/test/xml_datas/demo.xml new file mode 100644 index 0000000..13f9422 --- /dev/null +++ b/config/Datas/test/xml_datas/demo.xml @@ -0,0 +1,78 @@ + + true + 4 + 128 + 1122 + 112233445566 + 1.3 + 1112232.43123 + 112233 + 123 + 112334 + yf + + 1 + + C + + 1 + 2 + + + 1,2 + 1.2,2.3,3.4 + 1.2,2.2,3.2,4.3 + + 1970-01-01 00:00:00 + + + 1 + 2 + + + 1 + 2 + + + 1 + 2 + + + 1 + 2 + + + 1 + 2 + + + 1 + 2 + + + 1 + 3 + + + + 210 + 330 + + + + + 1 + true + + + 2 + false + + + + + 1 + 2 + + + \ No newline at end of file diff --git a/config/Defines/root.xml b/config/Defines/root.xml new file mode 100644 index 0000000..f9b27dd --- /dev/null +++ b/config/Defines/root.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/Defines/test.xml b/config/Defines/test.xml new file mode 100644 index 0000000..0b64bf5 --- /dev/null +++ b/config/Defines/test.xml @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + + + + + + + + 多态数据结构 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 多态数据结构 + + + + + + + + 使用;来分隔 + + + + + + + + + + + + + 最常见的普通 key-value表 + + + + + + + + + + + + + +
单例表,只有一个记录 + +
普通表,不过数据从tbrole_datas目录递归读入,每个文件是一个记录 + +
普通表,不过数据从tbrole_datas目录递归读入,每个文件是一个记录 + +
普通表,不过数据从tbrole_datas目录递归读入,每个文件是一个记录 + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 多态数据结构 + + + + + + + + + 使用;来分隔 + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + 支持在一个定义文件中 定义多个模块。 一般来说一个定义文件中一个模块比较好,但有些情况下为了方便可以定义多个。 + + + + + \ No newline at end of file diff --git a/config/生成code_cs_bin和data_bin.bat b/config/生成code_cs_bin和data_bin.bat new file mode 100644 index 0000000..fa7fc0b --- /dev/null +++ b/config/生成code_cs_bin和data_bin.bat @@ -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 \ No newline at end of file diff --git a/src/.dockerignore b/src/.dockerignore new file mode 100644 index 0000000..5967294 --- /dev/null +++ b/src/.dockerignore @@ -0,0 +1,2 @@ +**/bin/ +**/obj/ diff --git a/src/Luban.Client/.editorconfig b/src/Luban.Client/.editorconfig new file mode 100644 index 0000000..b5aa556 --- /dev/null +++ b/src/Luban.Client/.editorconfig @@ -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 diff --git a/src/Luban.Client/Luban.Client.csproj b/src/Luban.Client/Luban.Client.csproj new file mode 100644 index 0000000..352b043 --- /dev/null +++ b/src/Luban.Client/Luban.Client.csproj @@ -0,0 +1,16 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + diff --git a/src/Luban.Client/Properties/launchSettings.json b/src/Luban.Client/Properties/launchSettings.json new file mode 100644 index 0000000..4d18b70 --- /dev/null +++ b/src/Luban.Client/Properties/launchSettings.json @@ -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" + } + } +} \ No newline at end of file diff --git a/src/Luban.Client/Source/Net/GenClient.cs b/src/Luban.Client/Source/Net/GenClient.cs new file mode 100644 index 0000000..c54292b --- /dev/null +++ b/src/Luban.Client/Source/Net/GenClient.cs @@ -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 + { + + 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 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(), + }; + + 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(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(rpc, res); + } + + private void Process(QueryFilesExists p) + { + var root = p.Arg.Root; + var files = p.Arg.Files; + var re = new QueryFilesExistsRes() { Exists = new List(files.Count) }; + foreach (var f in files) + { + re.Exists.Add(File.Exists(Path.Combine(root, f))); + } + Session.ReplyRpc(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); + } + } +} diff --git a/src/Luban.Client/Source/Program.cs b/src/Luban.Client/Source/Program.cs new file mode 100644 index 0000000..f2a5575 --- /dev/null +++ b/src/Luban.Client/Source/Program.cs @@ -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 JobArguments { get; set; } = new List(); + + 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 ... [-- [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(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(); + + foreach (var fg in res.FileGroups) + { + tasks.Add(DownloadFileUtil.DownloadGeneratedFiles(fg.Dir, fg.Files)); + } + + Task.WaitAll(tasks.ToArray()); + return 0; + } + } +} diff --git a/src/Luban.Client/Source/Utils/CacheMetaManager.cs b/src/Luban.Client/Source/Utils/CacheMetaManager.cs new file mode 100644 index 0000000..6a51404 --- /dev/null +++ b/src/Luban.Client/Source/Utils/CacheMetaManager.cs @@ -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 _cacheFileMetas = new SortedDictionary(); + + 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 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 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 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); + } + } +} diff --git a/src/Luban.Client/Source/Utils/DownloadFileUtil.cs b/src/Luban.Client/Source/Utils/DownloadFileUtil.cs new file mode 100644 index 0000000..b5c9c60 --- /dev/null +++ b/src/Luban.Client/Source/Utils/DownloadFileUtil.cs @@ -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 newFiles) + { + List tasks = new List(); + 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(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(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); + } + } +} diff --git a/src/Luban.Client/Source/Utils/FileCleaner.cs b/src/Luban.Client/Source/Utils/FileCleaner.cs new file mode 100644 index 0000000..8051038 --- /dev/null +++ b/src/Luban.Client/Source/Utils/FileCleaner.cs @@ -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 _outputDirs = new HashSet(); + private readonly HashSet _savedFileOrDirs = new HashSet(); + private readonly HashSet _ignoreFileExtensions = new HashSet(); + + + 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(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 savedFiles) + { + var cleaner = new FileCleaner(); + cleaner.AddOutputDir(outputDir); + cleaner.AddIgnoreExtension("meta"); // for unity + foreach (var file in savedFiles) + { + cleaner.AddSavedFile(file.FilePath); + } + cleaner.RemoveUnusedFiles(); + } + + } +} diff --git a/src/Luban.Common/.editorconfig b/src/Luban.Common/.editorconfig new file mode 100644 index 0000000..ea2620e --- /dev/null +++ b/src/Luban.Common/.editorconfig @@ -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 diff --git a/src/Luban.Common/Luban.Common.csproj b/src/Luban.Common/Luban.Common.csproj new file mode 100644 index 0000000..2cc6075 --- /dev/null +++ b/src/Luban.Common/Luban.Common.csproj @@ -0,0 +1,11 @@ + + + + netcoreapp3.1 + + + + + + + diff --git a/src/Luban.Common/Source/EErrorCode.cs b/src/Luban.Common/Source/EErrorCode.cs new file mode 100644 index 0000000..d96e1f2 --- /dev/null +++ b/src/Luban.Common/Source/EErrorCode.cs @@ -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, + } +} diff --git a/src/Luban.Common/Source/Protos/FileInfo.cs b/src/Luban.Common/Source/Protos/FileInfo.cs new file mode 100644 index 0000000..5493015 --- /dev/null +++ b/src/Luban.Common/Source/Protos/FileInfo.cs @@ -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(); + } + } +} diff --git a/src/Luban.Common/Source/Protos/GenJob.cs b/src/Luban.Common/Source/Protos/GenJob.cs new file mode 100644 index 0000000..94383e6 --- /dev/null +++ b/src/Luban.Common/Source/Protos/GenJob.cs @@ -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 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()); + Verbose = os.ReadBool(); + } + } + + + public class FileGroup : BeanBase + { + public string Dir { get; set; } + + public List 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()); + } + } + + + public class GenJobRes : BeanBase + { + public EErrorCode ErrCode { get; set; } + + public string ErrMsg { get; set; } + + public List 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()); + } + } + + public class GenJob : Rpc + { + 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(); + } + } +} diff --git a/src/Luban.Common/Source/Protos/GetImportFileOrDirectory.cs b/src/Luban.Common/Source/Protos/GetImportFileOrDirectory.cs new file mode 100644 index 0000000..f54966b --- /dev/null +++ b/src/Luban.Common/Source/Protos/GetImportFileOrDirectory.cs @@ -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 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()); + } + } + + public class GetImportFileOrDirectory : Rpc + { + 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(); + } + } +} diff --git a/src/Luban.Common/Source/Protos/GetInputFile.cs b/src/Luban.Common/Source/Protos/GetInputFile.cs new file mode 100644 index 0000000..6ea9c0b --- /dev/null +++ b/src/Luban.Common/Source/Protos/GetInputFile.cs @@ -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 + { + 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(); + } + } +} diff --git a/src/Luban.Common/Source/Protos/GetOutputFile.cs b/src/Luban.Common/Source/Protos/GetOutputFile.cs new file mode 100644 index 0000000..e61849d --- /dev/null +++ b/src/Luban.Common/Source/Protos/GetOutputFile.cs @@ -0,0 +1,80 @@ +using Bright.Net.Codecs; +using Bright.Serialization; + +namespace Luban.Common.Protos +{ + + public class GetOutputFile : Rpc + { + 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(); + } + } +} diff --git a/src/Luban.Common/Source/Protos/ProtocolStub.cs b/src/Luban.Common/Source/Protos/ProtocolStub.cs new file mode 100644 index 0000000..4988b35 --- /dev/null +++ b/src/Luban.Common/Source/Protos/ProtocolStub.cs @@ -0,0 +1,19 @@ +using Bright.Net.Codecs; +using System.Collections.Generic; + +namespace Luban.Common.Protos +{ + public static class ProtocolStub + { + public static Dictionary Factories { get; } = new Dictionary + { + [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(), + }; + } +} diff --git a/src/Luban.Common/Source/Protos/PushException.cs b/src/Luban.Common/Source/Protos/PushException.cs new file mode 100644 index 0000000..5e66ac6 --- /dev/null +++ b/src/Luban.Common/Source/Protos/PushException.cs @@ -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(); + } + } +} diff --git a/src/Luban.Common/Source/Protos/PushLog.cs b/src/Luban.Common/Source/Protos/PushLog.cs new file mode 100644 index 0000000..12c8323 --- /dev/null +++ b/src/Luban.Common/Source/Protos/PushLog.cs @@ -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(); + } + } +} diff --git a/src/Luban.Common/Source/Protos/QueryFilesExists.cs b/src/Luban.Common/Source/Protos/QueryFilesExists.cs new file mode 100644 index 0000000..f9b2af0 --- /dev/null +++ b/src/Luban.Common/Source/Protos/QueryFilesExists.cs @@ -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 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()); + } + } + + public class QueryFilesExistsRes : BeanBase + { + public List 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(); + for (int i = 0; i < n; i++) + { + Exists.Add(os.ReadBool()); + } + } + } + + public class QueryFilesExists : Rpc + { + 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(); + } + } +} diff --git a/src/Luban.Common/Source/Utils/FileUtil.cs b/src/Luban.Common/Source/Utils/FileUtil.cs new file mode 100644 index 0000000..07b393b --- /dev/null +++ b/src/Luban.Common/Source/Utils/FileUtil.cs @@ -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); + } + + /// + /// 忽略以 文件名以 '.' '_' '~' 开头的文件 + /// + /// + /// + 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 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; + } + } +} diff --git a/src/Luban.Common/Source/Utils/LogUtil.cs b/src/Luban.Common/Source/Utils/LogUtil.cs new file mode 100644 index 0000000..3c99316 --- /dev/null +++ b/src/Luban.Common/Source/Utils/LogUtil.cs @@ -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; + } + } +} diff --git a/src/Luban.Common/Source/Utils/ProfileTimer.cs b/src/Luban.Common/Source/Utils/ProfileTimer.cs new file mode 100644 index 0000000..70d65f3 --- /dev/null +++ b/src/Luban.Common/Source/Utils/ProfileTimer.cs @@ -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 phaseStack = new Stack(); + + 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; + } + } +} diff --git a/src/Luban.Common/Source/Utils/TypeUtil.cs b/src/Luban.Common/Source/Utils/TypeUtil.cs new file mode 100644 index 0000000..c2b5aba --- /dev/null +++ b/src/Luban.Common/Source/Utils/TypeUtil.cs @@ -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 path) + { + var reverse = new List(); + 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; + } + + /// + /// the return value in range [offset, 2^14) + /// + /// + /// + 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 s_reserveNames = new HashSet() + { + "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; + } + } +} diff --git a/src/Luban.Common/Source/Utils/XmlUtil.cs b/src/Luban.Common/Source/Utils/XmlUtil.cs new file mode 100644 index 0000000..920fda2 --- /dev/null +++ b/src/Luban.Common/Source/Utils/XmlUtil.cs @@ -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)); + } + } +} diff --git a/src/Luban.Job.Cfg/Luban.Job.Cfg.csproj b/src/Luban.Job.Cfg/Luban.Job.Cfg.csproj new file mode 100644 index 0000000..dc8ade6 --- /dev/null +++ b/src/Luban.Job.Cfg/Luban.Job.Cfg.csproj @@ -0,0 +1,23 @@ + + + + Library + netcoreapp3.1 + + + + + + + + + + + + + + + + + + diff --git a/src/Luban.Job.Cfg/Source/Cache/FileRecordCacheManager.cs b/src/Luban.Job.Cfg/Source/Cache/FileRecordCacheManager.cs new file mode 100644 index 0000000..d21f1fc --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Cache/FileRecordCacheManager.cs @@ -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 +{ + /// + /// 配置加载记录缓存。 + /// 如果某个表对应的数据文件未修改,定义没变化,那加载后的数据应该是一样的。 + /// + 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)> _caches = new ConcurrentDictionary<(string, string, string, bool), (DefTable, List)>(); + + public bool TryGetCacheLoadedRecords(DefTable table, string md5, string originFile, string sheetName, bool exportTestData, out List 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().IsDefineEquals(table.ValueTType.GetBeanAs())) + { + 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 cacheRecords) + { + _caches[(table.Assembly.TimeZone.Id, md5, sheetName, exportTestData)] = (table, cacheRecords); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/DataSources/AbstractDataSource.cs b/src/Luban.Job.Cfg/Source/DataSources/AbstractDataSource.cs new file mode 100644 index 0000000..f6d53a0 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/DataSources/AbstractDataSource.cs @@ -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 ReadMulti(TBean type); + + public abstract void Load(string rawUrl, string sheetName, Stream stream, bool exportDebugData); + } +} diff --git a/src/Luban.Job.Cfg/Source/DataSources/Binary/BinaryDataSource.cs b/src/Luban.Job.Cfg/Source/DataSources/Binary/BinaryDataSource.cs new file mode 100644 index 0000000..43ecb78 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/DataSources/Binary/BinaryDataSource.cs @@ -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 ReadMulti(TBean type) + { + throw new NotImplementedException(); + } + + public override DType ReadOne(TBean type) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/DataSources/DataSourceFactory.cs b/src/Luban.Job.Cfg/Source/DataSources/DataSourceFactory.cs new file mode 100644 index 0000000..8835d5b --- /dev/null +++ b/src/Luban.Job.Cfg/Source/DataSources/DataSourceFactory.cs @@ -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); + } + } + } +} diff --git a/src/Luban.Job.Cfg/Source/DataSources/Excel/ExcelDataSource.cs b/src/Luban.Job.Cfg/Source/DataSources/Excel/ExcelDataSource.cs new file mode 100644 index 0000000..f420768 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/DataSources/Excel/ExcelDataSource.cs @@ -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 _sheets = new List(); + + + + 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 ReadMulti(TBean type) + { + var datas = new List(); + 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}"); + } + } + } +} diff --git a/src/Luban.Job.Cfg/Source/DataSources/Excel/ExcelStream.cs b/src/Luban.Job.Cfg/Source/DataSources/Excel/ExcelStream.cs new file mode 100644 index 0000000..25e106a --- /dev/null +++ b/src/Luban.Job.Cfg/Source/DataSources/Excel/ExcelStream.cs @@ -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 _datas; + private readonly int _toIndex; + private int _curIndex; + + public ExcelStream(List 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(); + 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 { cell }; + this._toIndex = 0; + this._curIndex = 0; + } + else + { + this._datas = new List(); + 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; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/DataSources/Excel/Sheet.cs b/src/Luban.Job.Cfg/Source/DataSources/Excel/Sheet.cs new file mode 100644 index 0000000..90b3369 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/DataSources/Excel/Sheet.cs @@ -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> _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 SubTitles { get; set; } = new Dictionary(); + + public List 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); + } + } + } +} diff --git a/src/Luban.Job.Cfg/Source/DataSources/Json/JsonDataSource.cs b/src/Luban.Job.Cfg/Source/DataSources/Json/JsonDataSource.cs new file mode 100644 index 0000000..5c83b33 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/DataSources/Json/JsonDataSource.cs @@ -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); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/DataSources/Lua/LuaDataSource.cs b/src/Luban.Job.Cfg/Source/DataSources/Lua/LuaDataSource.cs new file mode 100644 index 0000000..e6a74c5 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/DataSources/Lua/LuaDataSource.cs @@ -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); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/DataSources/Xml/XmlDataSource.cs b/src/Luban.Job.Cfg/Source/DataSources/Xml/XmlDataSource.cs new file mode 100644 index 0000000..d3736e2 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/DataSources/Xml/XmlDataSource.cs @@ -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); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/DataVisitors/BinaryExportor.cs b/src/Luban.Job.Cfg/Source/DataVisitors/BinaryExportor.cs new file mode 100644 index 0000000..9b40860 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/DataVisitors/BinaryExportor.cs @@ -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); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/DataVisitors/IDataActionVisitor.cs b/src/Luban.Job.Cfg/Source/DataVisitors/IDataActionVisitor.cs new file mode 100644 index 0000000..ba49db3 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/DataVisitors/IDataActionVisitor.cs @@ -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); + } +} diff --git a/src/Luban.Job.Cfg/Source/DataVisitors/IDataFuncVisitor.cs b/src/Luban.Job.Cfg/Source/DataVisitors/IDataFuncVisitor.cs new file mode 100644 index 0000000..e9b8c61 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/DataVisitors/IDataFuncVisitor.cs @@ -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); + } +} diff --git a/src/Luban.Job.Cfg/Source/DataVisitors/IsDefaultValue.cs b/src/Luban.Job.Cfg/Source/DataVisitors/IsDefaultValue.cs new file mode 100644 index 0000000..2602a18 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/DataVisitors/IsDefaultValue.cs @@ -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; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/DataVisitors/JsonExportor.cs b/src/Luban.Job.Cfg/Source/DataVisitors/JsonExportor.cs new file mode 100644 index 0000000..3320a59 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/DataVisitors/JsonExportor.cs @@ -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); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/DataVisitors/LuaExportor.cs b/src/Luban.Job.Cfg/Source/DataVisitors/LuaExportor.cs new file mode 100644 index 0000000..d697e70 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/DataVisitors/LuaExportor.cs @@ -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); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/DataVisitors/ResourceExportor.cs b/src/Luban.Job.Cfg/Source/DataVisitors/ResourceExportor.cs new file mode 100644 index 0000000..ff35393 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/DataVisitors/ResourceExportor.cs @@ -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(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/DataVisitors/ToStringVisitor.cs b/src/Luban.Job.Cfg/Source/DataVisitors/ToStringVisitor.cs new file mode 100644 index 0000000..167a08c --- /dev/null +++ b/src/Luban.Job.Cfg/Source/DataVisitors/ToStringVisitor.cs @@ -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()); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/DataVisitors/ValidatorVisitor.cs b/src/Luban.Job.Cfg/Source/DataVisitors/ValidatorVisitor.cs new file mode 100644 index 0000000..5947025 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/DataVisitors/ValidatorVisitor.cs @@ -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(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Datas/DArray.cs b/src/Luban.Job.Cfg/Source/Datas/DArray.cs new file mode 100644 index 0000000..31d3e31 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Datas/DArray.cs @@ -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); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Datas/DBean.cs b/src/Luban.Job.Cfg/Source/Datas/DBean.cs new file mode 100644 index 0000000..2190ac7 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Datas/DBean.cs @@ -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); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Datas/DBool.cs b/src/Luban.Job.Cfg/Source/Datas/DBool.cs new file mode 100644 index 0000000..2eefee2 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Datas/DBool.cs @@ -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(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Datas/DByte.cs b/src/Luban.Job.Cfg/Source/Datas/DByte.cs new file mode 100644 index 0000000..fec0d46 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Datas/DByte.cs @@ -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(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Datas/DBytes.cs b/src/Luban.Job.Cfg/Source/Datas/DBytes.cs new file mode 100644 index 0000000..03a379b --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Datas/DBytes.cs @@ -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); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Datas/DDateTime.cs b/src/Luban.Job.Cfg/Source/Datas/DDateTime.cs new file mode 100644 index 0000000..a1c658c --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Datas/DDateTime.cs @@ -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); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Datas/DDouble.cs b/src/Luban.Job.Cfg/Source/Datas/DDouble.cs new file mode 100644 index 0000000..2964b27 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Datas/DDouble.cs @@ -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(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Datas/DEnum.cs b/src/Luban.Job.Cfg/Source/Datas/DEnum.cs new file mode 100644 index 0000000..5829166 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Datas/DEnum.cs @@ -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(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Datas/DFint.cs b/src/Luban.Job.Cfg/Source/Datas/DFint.cs new file mode 100644 index 0000000..0a9fe3c --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Datas/DFint.cs @@ -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(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Datas/DFloat.cs b/src/Luban.Job.Cfg/Source/Datas/DFloat.cs new file mode 100644 index 0000000..fca2b39 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Datas/DFloat.cs @@ -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(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Datas/DFlong.cs b/src/Luban.Job.Cfg/Source/Datas/DFlong.cs new file mode 100644 index 0000000..9cdd929 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Datas/DFlong.cs @@ -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(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Datas/DFshort.cs b/src/Luban.Job.Cfg/Source/Datas/DFshort.cs new file mode 100644 index 0000000..41ba1fe --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Datas/DFshort.cs @@ -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(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Datas/DInt.cs b/src/Luban.Job.Cfg/Source/Datas/DInt.cs new file mode 100644 index 0000000..606caed --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Datas/DInt.cs @@ -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(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Datas/DList.cs b/src/Luban.Job.Cfg/Source/Datas/DList.cs new file mode 100644 index 0000000..e435825 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Datas/DList.cs @@ -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; + // } + //} + } +} diff --git a/src/Luban.Job.Cfg/Source/Datas/DLong.cs b/src/Luban.Job.Cfg/Source/Datas/DLong.cs new file mode 100644 index 0000000..45c66c4 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Datas/DLong.cs @@ -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(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Datas/DMap.cs b/src/Luban.Job.Cfg/Source/Datas/DMap.cs new file mode 100644 index 0000000..ac43607 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Datas/DMap.cs @@ -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); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Datas/DSet.cs b/src/Luban.Job.Cfg/Source/Datas/DSet.cs new file mode 100644 index 0000000..464c668 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Datas/DSet.cs @@ -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); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Datas/DShort.cs b/src/Luban.Job.Cfg/Source/Datas/DShort.cs new file mode 100644 index 0000000..2a4e0f4 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Datas/DShort.cs @@ -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(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Datas/DString.cs b/src/Luban.Job.Cfg/Source/Datas/DString.cs new file mode 100644 index 0000000..efc757b --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Datas/DString.cs @@ -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(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Datas/DText.cs b/src/Luban.Job.Cfg/Source/Datas/DText.cs new file mode 100644 index 0000000..c898a9f --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Datas/DText.cs @@ -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(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Datas/DType.cs b/src/Luban.Job.Cfg/Source/Datas/DType.cs new file mode 100644 index 0000000..24456cd --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Datas/DType.cs @@ -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; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Datas/DVector2.cs b/src/Luban.Job.Cfg/Source/Datas/DVector2.cs new file mode 100644 index 0000000..993422d --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Datas/DVector2.cs @@ -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(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Datas/DVector3.cs b/src/Luban.Job.Cfg/Source/Datas/DVector3.cs new file mode 100644 index 0000000..4d34b38 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Datas/DVector3.cs @@ -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(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Datas/DVector4.cs b/src/Luban.Job.Cfg/Source/Datas/DVector4.cs new file mode 100644 index 0000000..b5182ed --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Datas/DVector4.cs @@ -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(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Defs/CfgDefLoader.cs b/src/Luban.Job.Cfg/Source/Defs/CfgDefLoader.cs new file mode 100644 index 0000000..37b40d9 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Defs/CfgDefLoader.cs @@ -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); + } + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Defs/CfgDefTypeBase.cs b/src/Luban.Job.Cfg/Source/Defs/CfgDefTypeBase.cs new file mode 100644 index 0000000..e555275 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Defs/CfgDefTypeBase.cs @@ -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); + + } +} diff --git a/src/Luban.Job.Cfg/Source/Defs/DefAssembly.cs b/src/Luban.Job.Cfg/Source/Defs/DefAssembly.cs new file mode 100644 index 0000000..97613f7 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Defs/DefAssembly.cs @@ -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; + } + } + } + + } + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Defs/DefBean.cs b/src/Luban.Job.Cfg/Source/Defs/DefBean.cs new file mode 100644 index 0000000..b93cd1a --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Defs/DefBean.cs @@ -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(); + } + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Defs/DefField.cs b/src/Luban.Job.Cfg/Source/Defs/DefField.cs new file mode 100644 index 0000000..c1b1038 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Defs/DefField.cs @@ -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()} 不一致"); + } + + } + } + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Defs/DefTable.cs b/src/Luban.Job.Cfg/Source/Defs/DefTable.cs new file mode 100644 index 0000000..5acd608 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Defs/DefTable.cs @@ -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}"); + } + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Defs/TTypeTemplateExtends.cs b/src/Luban.Job.Cfg/Source/Defs/TTypeTemplateExtends.cs new file mode 100644 index 0000000..8b30af2 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Defs/TTypeTemplateExtends.cs @@ -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); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Generate/CodeRenderBase.cs b/src/Luban.Job.Cfg/Source/Generate/CodeRenderBase.cs new file mode 100644 index 0000000..9dd75a6 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Generate/CodeRenderBase.cs @@ -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}"); + } + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Generate/CppBinCodeRender.cs b/src/Luban.Job.Cfg/Source/Generate/CppBinCodeRender.cs new file mode 100644 index 0000000..1bbfba7 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Generate/CppBinCodeRender.cs @@ -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, + }); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Generate/CsBinCodeRender.cs b/src/Luban.Job.Cfg/Source/Generate/CsBinCodeRender.cs new file mode 100644 index 0000000..392356d --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Generate/CsBinCodeRender.cs @@ -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; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Generate/CsCodeRenderBase.cs b/src/Luban.Job.Cfg/Source/Generate/CsCodeRenderBase.cs new file mode 100644 index 0000000..8283364 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Generate/CsCodeRenderBase.cs @@ -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); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Generate/CsJsonCodeRender.cs b/src/Luban.Job.Cfg/Source/Generate/CsJsonCodeRender.cs new file mode 100644 index 0000000..833a0b3 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Generate/CsJsonCodeRender.cs @@ -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; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Generate/EditorCppRender.cs b/src/Luban.Job.Cfg/Source/Generate/EditorCppRender.cs new file mode 100644 index 0000000..220972f --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Generate/EditorCppRender.cs @@ -0,0 +1,47 @@ +using Luban.Job.Cfg.Defs; +using Luban.Job.Common.Defs; +using System; +using System.Collections.Generic; + +namespace Luban.Job.Cfg.Generate +{ + class EditorCppRender + { + 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}"); + } + } + + public string Render(DefConst c) + { + return "// const"; + } + + public string Render(DefEnum e) + { + return "// enum"; + } + + public string Render(DefBean b) + { + return "// bean"; + } + + public string Render(DefTable p) + { + return "// table"; + } + + public string RenderStubs(string name, string module, List<CfgDefTypeBase> protos) + { + return "// stubs"; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Generate/EditorCsRender.cs b/src/Luban.Job.Cfg/Source/Generate/EditorCsRender.cs new file mode 100644 index 0000000..adc55f2 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Generate/EditorCsRender.cs @@ -0,0 +1,237 @@ +using Luban.Job.Cfg.Defs; +using Luban.Job.Common.Defs; +using Luban.Job.Common.Utils; +using Scriban; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Luban.Job.Cfg.Generate +{ + class EditorCsRender + { + 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); editor 不需要生成 table 的定义 + // case CTable r: return Render(r); + default: throw new Exception($"unknown render type:{o}"); + } + } + + public string Render(DefConst c) + { + return RenderUtil.RenderCsConstClass(c); + } + + public string Render(DefEnum e) + { + return RenderUtil.RenderCsEnumClass(e); + } + + [ThreadStatic] + private static Template t_beanRender; + public string Render(DefBean b) + { + var template = t_beanRender ??= Template.Parse(@" +using Bright.Serialization; + +namespace {{namespace}} +{ + +public {{cs_class_modifier}} class {{name}} : {{if parent_def_type}} {{parent}} {{else}} ISerializable {{if is_abstract_type}}, ITypeId {{end}} {{end}} +{ + public {{name}}() {{if parent_def_type}} : base() {{end}} + { + + } + + public {{name}}(Bright.Common.NotNullInitialization _) {{if parent_def_type}} : base(_) {{end}} + { + {{- for field in fields }} + {{if field.ctype.need_init}}{{field.proto_cs_init_field}} {{end}} + {{-end}} + } + + {{if is_abstract_type}} + public static void Serialize{{name}}(ByteBuf _buf, {{name}} x) + { + if (x == null) { _buf.WriteInt(0); return; } + _buf.WriteInt(x.GetTypeId()); + x.Serialize(_buf); + } + + public static {{name}} Deserialize{{name}}(ByteBuf _buf) + { + {{name}} x; + switch (_buf.ReadInt()) + { + case 0 : return null; + {{- for child in hierarchy_not_abstract_children}} + case {{child.full_name}}.ID: x = new {{child.full_name}}(false); break; + {{-end}} + default: throw new SerializationException(); + } + x.Deserialize(_buf); + return x; + } + {{end}} + {{- for field in fields }} + public {{field.ctype.cs_define_type}} {{field.cs_style_name}}; + {{-end}} + + {{if !parent_def_type && is_abstract_type}} + public abstract int GetTypeId(); + {{end}} + {{if parent_def_type && !is_abstract_type}} + public const int ID = {{id}}; + public override int GetTypeId() + { + return ID; + } + {{end}} + + public {{cs_method_modifer}} void Serialize(ByteBuf _buf) + { + {{if parent_def_type}} base.Serialize(_buf); {{end}} + {{- for field in fields }} + {{field.cs_serialize}} + {{-end}} + } + + public {{cs_method_modifer}} void Deserialize(ByteBuf _buf) + { + {{if parent_def_type}} base.Deserialize(_buf); {{end}} + {{- for field in fields }} + {{field.cs_deserialize}} + {{-end}} + } + + public override string ToString() + { + return ""{{full_name}}{ "" + {{- for field in hierarchy_fields }} + + ""{{field.cs_style_name}}:"" + {{field.proto_cs_to_string}} + "","" + {{-end}} + + ""}""; + } + } + +} + +"); + var result = template.Render(b); + + return result; + } + + [ThreadStatic] + private static Template t_tableRender; + public string Render(DefTable p) + { + var template = t_tableRender ??= Template.Parse(@" +using Bright.Serialization; + +namespace {{namespace}} +{ + +public sealed class {{name}} : Bright.Net.Protocol +{ + {{- for field in fields }} + public {{field.ctype.cs_define_type}} {{field.cs_style_name}}; + {{-end}} + public {{name}}() + { + } + public {{name}}(Bright.Common.NotNullInitialization _) + { + {{- for field in fields }} + {{if field.ctype.need_init}}{{field.proto_cs_init_field}} {{end}} + {{-end}} + } + public const int ID = {{id}}; + + public override int GetTypeId() + { + return ID; + } + + public override void Serialize(ByteBuf _buf) + { + {{- for field in fields }} + {{field.cs_serialize}} + {{-end}} + } + + public override void Deserialize(ByteBuf _buf) + { + {{- for field in fields }} + {{field.cs_deserialize}} + {{-end}} + } + + public override void Reset() + { + throw new System.NotImplementedException(); + } + + public override object Clone() + { + throw new System.NotImplementedException(); + } + + public override string ToString() + { + return ""{{full_name}}{ "" + {{- for field in fields }} + + ""{{field.cs_style_name}}:"" + {{field.proto_cs_to_string}} + "","" + {{-end}} + + ""}""; + } +} + +} + +"); + var result = template.Render(p); + + return result; + } + + [ThreadStatic] + private static Template t_stubRender; + public string RenderStubs(string name, string module, List<CfgDefTypeBase> protos) + { + var template = t_stubRender ??= Template.Parse(@" +using Bright.Serialization; + +namespace {{namespace}} +{ + +public static class {{name}} +{ + public static System.Collections.Generic.Dictionary<int, Bright.Net.IProtocolFactory> Factories { get; } = new System.Collections.Generic.Dictionary<int, Bright.Net.IProtocolFactory> + { + {{- for proto in protos }} + [{{proto.full_name}}.ID] = () => new {{proto.full_name}}(false), + {{-end}} + }; +} + +} + +"); + var result = template.Render(new + { + Name = name, + Namespace = module, + Tables = protos.Where(p => p is DefTable).ToList(), + }); + + return result; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Generate/GoCodeRender.cs b/src/Luban.Job.Cfg/Source/Generate/GoCodeRender.cs new file mode 100644 index 0000000..46587a5 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Generate/GoCodeRender.cs @@ -0,0 +1,336 @@ +using Luban.Job.Cfg.Defs; +using Luban.Job.Common.Defs; +using Scriban; +using System; +using System.Collections.Generic; + +namespace Luban.Job.Cfg.Generate +{ + class GoCodeRender + { + 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}"); + } + } + + [ThreadStatic] + private static Template t_constRender; + + public string Render(DefConst c) + { + string package = "cfg"; + + var template = t_constRender ??= Template.Parse(@" +package {{package}} + +const ( + {{- for item in x.items }} + {{x.go_full_name}}_{{item.name}} = {{go_const_value item.ctype item.value}} + {{-end}} +) +"); + var result = template.RenderCode(c, new Dictionary<string, object>() { ["package"] = package }); + + return result; + } + + [ThreadStatic] + private static Template t_enumRender; + + public string Render(DefEnum e) + { + string package = "cfg"; + + var template = t_enumRender ??= Template.Parse(@" +package {{package}} + +const ( + {{- for item in x.items }} + {{x.go_full_name}}_{{item.name}} = {{item.value}} + {{-end}} +) + +"); + var result = template.RenderCode(e, new Dictionary<string, object>() { ["package"] = package }); + + return result; + } + + [ThreadStatic] + private static Template t_beanRender; + + public string Render(DefBean b) + { + string package = "cfg"; + + var template = t_beanRender ??= Template.Parse(@" +{{- + go_full_name = x.go_full_name + parent_def_type = x.parent_def_type + is_abstract_type = x.is_abstract_type + export_fields = x.export_fields + hierarchy_not_abstract_children = x.hierarchy_not_abstract_children +-}} +package {{package}} + +import ""bright/serialization"" + +{{x.go_import}} + +type {{go_full_name}} struct { + {{if parent_def_type}}{{parent_def_type.go_full_name}}{{end}} + {{- for field in export_fields }} + {{field.cs_style_name}} {{go_define_type field.ctype}} + {{-end}} +} + +{{if !is_abstract_type}} +func ({{go_full_name}}) GetTypeId() int { + return {{x.id}} +} +{{end}} + +func New{{go_full_name}}(_buf *serialization.ByteBuf) (_v *{{go_full_name}}, err error) { + _v = &{{go_full_name}}{} +{{if parent_def_type}} + var _p *{{parent_def_type.go_full_name}} + if _p, err = New{{parent_def_type.go_full_name}}(_buf) ; err != nil { return } + _v.{{parent_def_type.go_full_name}} = *_p +{{end}} + {{- for field in export_fields }} + {{go_deserialize_field field '_buf'}} + {{-end}} + return +} +{{if is_abstract_type}} +func NewChild{{go_full_name}}(_buf *serialization.ByteBuf) (_v interface{}, err error) { + var id int32 + if id, err = _buf.ReadInt() ; err != nil { + return + } + switch id { + case 0 : return nil, nil + {{- for child in hierarchy_not_abstract_children}} + case {{child.id}}: return New{{child.go_full_name}}(_buf); + {{-end}} + } + return +} +{{end}} + +"); + var result = template.RenderCode(b, new Dictionary<string, object>() { ["package"] = package }); + + return result; + } + + + [ThreadStatic] + private static Template t_tableRender; + public string Render(DefTable p) + { + string package = "cfg"; + var template = t_tableRender ??= Template.Parse(@" +{{- + go_full_name = x.go_full_name + key_type = x.key_ttype + key_type1 = x.key_ttype1 + key_type2 = x.key_ttype2 + value_type = x.value_ttype + index_field = x.index_field + index_field1 = x.index_field1 + index_field2 = x.index_field2 +-}} +package {{package}} + +import ""bright/serialization"" + +{{if x.is_two_key_map_table}} + +type {{go_full_name}} struct { + _dataListMap map[{{go_define_type key_type1}}][]{{go_define_type value_type}} + _dataMapMap map[{{go_define_type key_type1}}]map[{{go_define_type key_type2}}]{{go_define_type value_type}} + _dataList []{{go_define_type value_type}} +} + +func New{{go_full_name}}(_buf *serialization.ByteBuf) (*{{go_full_name}}, error) { + if size, err := _buf.ReadSize() ; err != nil { + return nil, err + } else { + _dataList := make([]{{go_define_type value_type}}, 0, size) + _dataListMap := make(map[{{go_define_type key_type1}}][]{{go_define_type value_type}}) + _dataMapMap := make(map[{{go_define_type key_type1}}]map[{{go_define_type key_type2}}]{{go_define_type value_type}}) + + for i := 0 ; i < size ; i++ { + if _v, err2 := {{go_deserialize_type value_type '_buf'}}; err2 != nil { + return nil, err2 + } else { + _dataList = append(_dataList, _v) + + _dataMap := _dataMapMap[_v.{{index_field1.cs_style_name}}] + if _dataMap == nil { + _dataMap = make(map[{{go_define_type key_type2}}]{{go_define_type value_type}}) + _dataMapMap[_v.{{index_field1.cs_style_name}}] = _dataMap + } + _dataMap[_v.{{index_field2.cs_style_name}}] = _v + + + _dataList := _dataListMap[_v.{{index_field1.cs_style_name}}] + if _dataList == nil { + _dataList = make([]{{go_define_type value_type}}, 0) + } + _dataList = append(_dataList, _v) + _dataListMap[_v.{{index_field1.cs_style_name}}] = _dataList + } + } + return &{{go_full_name}}{_dataList:_dataList, _dataMapMap:_dataMapMap, _dataListMap:_dataListMap}, nil + } +} + +func (table *{{go_full_name}}) GetDataMapMap() map[{{go_define_type key_type1}}]map[{{go_define_type key_type2}}]{{go_define_type value_type}} { + return table._dataMapMap +} + +func (table *{{go_full_name}}) GetDataListMap() map[{{go_define_type key_type1}}][]{{go_define_type value_type}} { + return table._dataListMap +} + +func (table *{{go_full_name}}) GetDataList() []{{go_define_type value_type}} { + return table._dataList +} + +func (table *{{go_full_name}}) Get(key1 {{go_define_type key_type1}}, key2 {{go_define_type key_type2}}) {{go_define_type value_type}} { + if v , ok := table._dataMapMap[key1] ; ok { + return v[key2] + } else { + return nil + } +} + + +{{else if x.is_map_table }} +type {{go_full_name}} struct { + _dataMap map[{{go_define_type key_type}}]{{go_define_type value_type}} + _dataList []{{go_define_type value_type}} +} + +func New{{go_full_name}}(_buf *serialization.ByteBuf) (*{{go_full_name}}, error) { + if size, err := _buf.ReadSize() ; err != nil { + return nil, err + } else { + _dataList := make([]{{go_define_type value_type}}, 0, size) + dataMap := make(map[{{go_define_type key_type}}]{{go_define_type value_type}}) + + for i := 0 ; i < size ; i++ { + if _v, err2 := {{go_deserialize_type value_type '_buf'}}; err2 != nil { + return nil, err2 + } else { + _dataList = append(_dataList, _v) + dataMap[_v.{{index_field.cs_style_name}}] = _v + } + } + return &{{go_full_name}}{_dataList:_dataList, _dataMap:dataMap}, nil + } +} + +func (table *{{go_full_name}}) GetDataMap() map[{{go_define_type key_type}}]{{go_define_type value_type}} { + return table._dataMap +} + +func (table *{{go_full_name}}) GetDataList() []{{go_define_type value_type}} { + return table._dataList +} + +func (table *{{go_full_name}}) Get(key {{go_define_type key_type}}) {{go_define_type value_type}} { + return table._dataMap[key] +} + + +{{else}} + +import ""errors"" + +type {{go_full_name}} struct { + _data {{go_define_type value_type}} +} + +func New{{go_full_name}}(_buf *serialization.ByteBuf) (*{{go_full_name}}, error) { + if size, err := _buf.ReadSize() ; err != nil { + return nil, err + } else if size != 1 { + return nil, errors.New("" size != 1 "") + } else { + if _v, err2 := {{go_deserialize_type value_type '_buf'}}; err2 != nil { + return nil, err2 + } else { + return &{{go_full_name}}{_data:_v}, nil + } + } +} + +func (table *{{go_full_name}}) Get() {{go_define_type value_type}} { + return table._data +} + +{{end}} +"); + var result = template.RenderCode(p, new Dictionary<string, object>() { ["package"] = package }); + + return result; + } + + [ThreadStatic] + private static Template t_serviceRender; + public string RenderService(string name, string module, List<DefTable> tables) + { + string package = "cfg"; + + var template = t_serviceRender ??= Template.Parse(@" +package {{package}} + +import ""bright/serialization"" + +type ByteBufLoader func(string) (*serialization.ByteBuf, error) + +type {{name}} struct { + {{- for table in tables }} + {{table.name}} *{{table.go_full_name}} + {{-end}} +} + +func NewTables(loader ByteBufLoader) (*{{name}}, error) { + var err error + var buf *serialization.ByteBuf + + tables := &{{name}}{} + {{- for table in tables }} + if buf, err = loader(""{{table.output_data_file}}"") ; err != nil { + return nil, err + } + if tables.{{table.name}}, err = New{{table.go_full_name}}(buf) ; err != nil { + return nil, err + } + {{-end}} + return tables, nil +} + +"); + var result = template.Render(new + { + Name = name, + Namespace = module, + Tables = tables, + Package = package, + }); + + return result; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Generate/ICodeRender.cs b/src/Luban.Job.Cfg/Source/Generate/ICodeRender.cs new file mode 100644 index 0000000..c55fd18 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Generate/ICodeRender.cs @@ -0,0 +1,21 @@ +using Luban.Job.Cfg.Defs; +using Luban.Job.Common.Defs; +using System.Collections.Generic; + +namespace Luban.Job.Cfg.Generate +{ + interface ICodeRender + { + string RenderAny(object o); + + string Render(DefConst c); + + string Render(DefEnum c); + + string Render(DefBean b); + + string Render(DefTable c); + + string RenderService(string name, string module, List<DefTable> tables); + } +} diff --git a/src/Luban.Job.Cfg/Source/Generate/JavaBinCodeRender.cs b/src/Luban.Job.Cfg/Source/Generate/JavaBinCodeRender.cs new file mode 100644 index 0000000..fb9da94 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Generate/JavaBinCodeRender.cs @@ -0,0 +1,313 @@ +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 JavaBinCodeRender : CodeRenderBase + { + public override string Render(DefConst c) + { + return RenderUtil.RenderJavaConstClass(c); + } + + public override string Render(DefEnum c) + { + return RenderUtil.RenderJavaEnumClass(c); + } + + [ThreadStatic] + private static Template t_beanRender; + + public override string Render(DefBean b) + { + var template = t_beanRender ??= Template.Parse(@" +package {{x.namespace_with_top_module}}; + +import bright.serialization.*; + +{{ + name = x.name + parent_def_type = x.parent_def_type + export_fields = x.export_fields + hierarchy_export_fields = x.hierarchy_export_fields +}} + +public {{x.java_class_modifier}} class {{name}} extends {{if parent_def_type}} {{x.parent_def_type.full_name_with_top_module}} {{else}} bright.serialization.AbstractBean {{end}} +{ + public {{name}}(ByteBuf _buf) + { + {{~if parent_def_type~}} + super(_buf); + {{~end~}} + {{~ for field in export_fields ~}} + {{java_deserialize '_buf' field.java_style_name field.ctype}} + {{~if field.index_field~}} + for({{java_box_define_type field.ctype.element_type}} _v : {{field.java_style_name}}) + { + {{field.java_style_name}}_Index.put(_v.{{field.index_field.java_style_name}}, _v); + } + {{~end~}} + {{~end~}} + } + + public {{name}}({{- for field in hierarchy_export_fields }}{{java_define_type field.ctype}} {{field.name}}{{if !for.last}},{{end}} {{end}}) + { + {{~if parent_def_type~}} + super({{ for field in parent_def_type.hierarchy_export_fields }}{{field.name}}{{if !for.last}}, {{end}}{{end}}); + {{~end~}} + {{~ for field in export_fields ~}} + this.{{field.java_style_name}} = {{field.name}}; + {{~if field.index_field~}} + for({{java_box_define_type field.ctype.element_type}} _v : {{field.java_style_name}}) + { + {{field.java_style_name}}_Index.put(_v.{{field.index_field.java_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_with_top_module}}.ID: return new {{child.full_name_with_top_module}}(_buf); + {{-end}} + default: throw new SerializationException(); + } + {{else}} + return new {{name}}(_buf); + {{end}} + } + + {{~ for field in export_fields ~}} + public final {{java_define_type field.ctype}} {{field.java_style_name}}; + {{~if field.index_field~}} + public final java.util.HashMap<{{java_box_define_type field.index_field.ctype}}, {{java_box_define_type field.ctype.element_type}}> {{field.java_style_name}}_Index = new java.util.HashMap<>(); + {{~end~}} + {{~if field.gen_ref~}} + public {{field.java_ref_validator_define}} + {{~end~}} + {{~end~}} + +{{if !x.is_abstract_type}} + public static final int ID = {{x.id}}; + + @Override + public int getTypeId() { return ID; } +{{end}} + + @Override + public void serialize(ByteBuf os) + { + throw new UnsupportedOperationException(); + } + + @Override + public void deserialize(ByteBuf os) + { + throw new UnsupportedOperationException(); + } + + public void resolve(java.util.HashMap<String, Object> _tables) + { + {{~if parent_def_type}}super.resolve(_tables);{{end}} + {{~ for field in export_fields ~}} + {{~if field.gen_ref~}} + {{java_ref_validator_resolve field}} + {{~else if field.has_recursive_ref~}} + {{java_recursive_resolve field '_tables'}} + {{~end~}} + {{~end~}} + } + + @Override + public String toString() + { + return ""{{full_name}}{ "" + {{- for field in hierarchy_export_fields }} + + ""{{field.java_style_name}}:"" + {{java_to_string field.java_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(@" +package {{x.namespace_with_top_module}}; + +import bright.serialization.*; + +{{~ + name = x.name + key_type = x.key_ttype + key_type1 = x.key_ttype1 + key_type2 = x.key_ttype2 + value_type = x.value_ttype +~}} + +public final class {{name}} +{ + {{~ if x.is_two_key_map_table ~}} + private final java.util.HashMap<{{java_box_define_type key_type1}},java.util.ArrayList<{{java_box_define_type value_type}}>> _dataListMap; + private final java.util.HashMap<{{java_box_define_type key_type1}}, java.util.HashMap<{{java_box_define_type key_type2}}, {{java_box_define_type value_type}}>> _dataMapMap; + private final java.util.ArrayList<{{java_box_define_type value_type}}> _dataList; + + public {{name}}(ByteBuf _buf) + { + _dataListMap = new java.util.HashMap<{{java_box_define_type key_type1}},java.util.ArrayList<{{java_box_define_type value_type}}>>(); + _dataMapMap = new java.util.HashMap<{{java_box_define_type key_type1}}, java.util.HashMap<{{java_box_define_type key_type2}}, {{java_box_define_type value_type}}>>(); + _dataList = new java.util.ArrayList<{{java_box_define_type value_type}}>(); + + for(int n = _buf.readSize() ; n > 0 ; --n) + { + {{java_box_define_type value_type}} _v; + {{java_deserialize '_buf' '_v' value_type}} + _dataList.add(_v); + var _key = _v.{{x.index_field1.java_style_name}}; + var list = _dataListMap.computeIfAbsent(_key, k -> new java.util.ArrayList<>()); + list.add(_v); + var map = _dataMapMap.computeIfAbsent(_key, k -> new java.util.HashMap<>()); + map.put(_v.{{x.index_field2.java_style_name}}, _v); + } + } + + public java.util.HashMap<{{java_box_define_type key_type1}},java.util.ArrayList<{{java_box_define_type value_type}}>> getDataListMap() { return _dataListMap; } + public java.util.HashMap<{{java_box_define_type key_type1}}, java.util.HashMap<{{java_box_define_type key_type2}}, {{java_box_define_type value_type}}>> getDataMapMap() {return _dataMapMap;} + public java.util.ArrayList<{{java_box_define_type value_type}}> getDataList() { return _dataList; } + + {{if value_type.is_dynamic}} + public T getAs<T extends {{java_box_define_type value_type}}>({{java_define_type key_type1}} key1, {{java_define_type key_type2}} key2) { return (T)_dataMapMap.get(key1).get(key2); } + {{end}} + public {{java_box_define_type value_type}} get({{java_define_type key_type1}} key1, {{java_define_type key_type2}} key2) { return _dataMapMap.get(key1).get(key2);} + + public void resolve(java.util.HashMap<String, Object> _tables) + { + for({{java_box_define_type value_type}} v : _dataList) + { + v.resolve(_tables); + } + } + {{~else if x.is_map_table ~}} + private final java.util.HashMap<{{java_box_define_type key_type}}, {{java_box_define_type value_type}}> _dataMap; + private final java.util.ArrayList<{{java_box_define_type value_type}}> _dataList; + + public {{name}}(ByteBuf _buf) + { + _dataMap = new java.util.HashMap<{{java_box_define_type key_type}}, {{java_box_define_type value_type}}>(); + _dataList = new java.util.ArrayList<{{java_box_define_type value_type}}>(); + + for(int n = _buf.readSize() ; n > 0 ; --n) + { + {{java_box_define_type value_type}} _v; + {{java_deserialize '_buf' '_v' value_type}} + _dataList.add(_v); + _dataMap.put(_v.{{x.index_field.java_style_name}}, _v); + } + } + + public java.util.HashMap<{{java_box_define_type key_type}}, {{java_box_define_type value_type}}> getDataMap() { return _dataMap; } + public java.util.ArrayList<{{java_box_define_type value_type}}> getDataList() { return _dataList; } + +{{~if value_type.is_dynamic~}} + public T getAs<T extends {{java_box_define_type value_type}}>({{java_box_define_type key_type}} key) { return (T)_dataMap.get(key); } +{{~end~}} + public {{java_box_define_type value_type}} get({{java_define_type key_type}} key) { return _dataMap.get(key); } + + public void resolve(java.util.HashMap<String, Object> _tables) + { + for({{java_box_define_type value_type}} v : _dataList) + { + v.resolve(_tables); + } + } + + {{~else~}} + private final {{java_define_type value_type}} _data; + + public final {{java_define_type value_type}} data() { return _data; } + + public {{name}}(ByteBuf _buf) + { + int n = _buf.readSize(); + if (n != 1) throw new SerializationException(""table mode=one, but size != 1""); + {{java_deserialize '_buf' '_data' value_type}} + } + + + {{~ for field in value_type.bean.hierarchy_export_fields ~}} + public {{java_define_type field.ctype}} {{field.java_getter_name}}() { return _data.{{field.java_style_name}}; } + {{~end~}} + + public void resolve(java.util.HashMap<String, Object> _tables) + { + _data.resolve(_tables); + } + + {{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(@" +package {{package}}; + +import bright.serialization.*; + +public final class {{name}} +{ + public static interface IByteBufLoader { + ByteBuf load(String file) throws java.io.IOException; + } + + {{- for table in tables }} + public final {{table.full_name_with_top_module}} {{table.name}}; + {{-end}} + + public {{name}}(IByteBufLoader loader) throws java.io.IOException { + var tables = new java.util.HashMap<String, Object>(); + {{- for table in tables }} + {{table.name}} = new {{table.full_name_with_top_module}}(loader.load(""{{table.output_data_file}}"")); + tables.put(""{{table.full_name}}"", {{table.name}}); + {{-end}} + + {{- for table in tables }} + {{table.name}}.resolve(tables); + {{-end}} + } +} + +"); + var result = template.Render(new + { + Name = name, + Package = module, + Tables = tables, + }); + + return result; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Generate/LuaRender.cs b/src/Luban.Job.Cfg/Source/Generate/LuaRender.cs new file mode 100644 index 0000000..a3574b2 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Generate/LuaRender.cs @@ -0,0 +1,264 @@ +using Luban.Job.Cfg.Defs; +using Luban.Job.Common.Defs; +using Scriban; +using System.Collections.Generic; +using System.Linq; + +namespace Luban.Job.Cfg.Generate +{ + class LuaRender + { + + public string RenderAll(List<DefTypeBase> types) + { + var consts = types.Where(t => t is DefConst).ToList(); + var enums = types.Where(t => t is DefEnum).ToList(); + var beans = types.Where(t => t is DefBean).ToList(); + var tables = types.Where(t => t is DefTable).ToList(); + var template = Template.Parse(@" +{{ + consts = x.consts + enums = x.enums + beans = x.beans + tables = x.tables +}} +local setmetatable = setmetatable +local pairs = pairs +local ipairs = ipairs +local tinsert = table.insert + +local function SimpleClass() + local class = {} + class.__index = class + class.New = function(...) + local ctor = class.ctor + local o = ctor and ctor(...) or {} + setmetatable(o, class) + return o + end + return class +end + + +local function get_map_size(m) + local n = 0 + for _ in pairs(m) do + n = n + 1 + end + return n +end + +local consts = +{ + {{~ for c in consts ~}} + ---@class {{c.full_name}} + {{~ for item in c.items ~}} + ---@field public {{item.name}} {{item.type}} + {{~end~}} + ['{{c.full_name}}'] = { {{ for item in c.items }} {{item.name}}={{lua_const_value item.ctype item.value}}, {{end}} }; + {{~end~}} +} + +local enums = +{ + {{~ for c in enums ~}} + ---@class {{c.full_name}} + {{~ for item in c.items ~}} + ---@field public {{item.name}} int + {{~end~}} + ['{{c.full_name}}'] = { {{ for item in c.items }} {{item.name}}={{item.int_value}}, {{end}} }; + {{~end~}} +} + + +local function InitTypes(methods) + local readBool = methods.readBool + local readByte = methods.readByte + local readShort = methods.readShort + local readFshort = methods.readFshort + local readInt = methods.readInt + local readFint = methods.readFint + local readLong = methods.readLong + local readFlong = methods.readFlong + local readFloat = methods.readFloat + local readDouble = methods.readDouble + local readSize = methods.readSize + + local readString = methods.readString + + local function readVector2(bs) + return { x = readFloat(bs), y = readFloat(bs) } + end + + local function readVector3(bs) + return { x = readFloat(bs), y = readFloat(bs), z = readFloat(bs) } + end + + local function readVector4(bs) + return { x = readFloat(bs), y = readFloat(bs), z = readFloat(bs), w = readFloat(bs) } + end + + local function readList(bs, keyFun) + local list = {} + local v + for i = 1, readSize(bs) do + tinsert(list, keyFun(bs)) + end + return list + end + + local readArray = readList + + local function readSet(bs, keyFun) + local set = {} + local v + for i = 1, readSize(bs) do + tinsert(set, keyFun(bs)) + end + return set + end + + local function readMap(bs, keyFun, valueFun) + local map = {} + for i = 1, readSize(bs) do + local k = keyFun(bs) + local v = valueFun(bs) + map[k] = v + end + return map + end + + local function readNullableBool(bs) + if readBool(bs) then + return readBool(bs) + end + end + + local beans = {} +{{~ for bean in beans ~}} + do + ---@class {{bean.full_name}} {{if bean.parent_def_type}}:{{bean.parent}} {{end}} + {{~ for field in bean.export_fields~}} + ---@field public {{field.name}} {{lua_comment_type field.ctype}} + {{~end~}} + local class = SimpleClass() + class._id = {{bean.id}} + class._name = '{{bean.full_name}}' + local id2name = { {{for c in bean.hierarchy_not_abstract_children}} [{{c.id}}] = '{{c.full_name}}', {{end}} } +{{~if bean.is_abstract_type~}} + class._deserialize = function(bs) + local id = readInt(bs) + if id ~= 0 then return beans[id2name[id]]._deserialize(bs) end + end +{{~else~}} + class._deserialize = function(bs) + local o = { + {{~ for field in bean.hierarchy_export_fields ~}} + {{~if !field.need_marshal_bool_prefix~}} + {{field.name}} = {{lua_undering_deserialize 'bs' field.ctype}}, + {{~else~}} + {{field.name}} = {{if !field.ctype.is_bool}}readBool(bs) and {{lua_undering_deserialize 'bs' field.ctype}} or nil {{-else-}} readNullableBool(bs) {{-end-}}, + {{~end~}} + {{~end~}} + } + setmetatable(o, class) + return o + end +{{~end~}} + beans[class._name] = class + end +{{~end~}} + + local tables = + { +{{~for table in tables ~}} + {{~if table.is_two_key_map_table~}} + { name='{{table.name}}', file='{{table.output_data_file}}', mode='bmap', index1='{{table.index1}}', index2='{{table.index2}}', value_type='{{table.value_ttype.bean.full_name}}' }, + {{~else if table.is_map_table ~}} + { name='{{table.name}}', file='{{table.output_data_file}}', mode='map', index='{{table.index}}', value_type='{{table.value_ttype.bean.full_name}}' }, + {{~else~}} + { name='{{table.name}}', file='{{table.output_data_file}}', mode='one', value_type='{{table.value_ttype.bean.full_name}}'}, + {{~end~}} +{{~end~}} + } + return { consts = consts, enums = enums, beans = beans, tables = tables } + end + +return { InitTypes = InitTypes } + + +"); + return template.RenderCode(new { Consts = consts, Enums = enums, Beans = beans, Tables = tables }); + } + + public string RenderDefines(List<DefTypeBase> types) + { + var consts = types.Where(t => t is DefConst).ToList(); + var enums = types.Where(t => t is DefEnum).ToList(); + var beans = types.Where(t => t is DefBean).ToList(); + var tables = types.Where(t => t is DefTable).ToList(); + var template = Template.Parse(@" +{{ + consts = x.consts + enums = x.enums + beans = x.beans + tables = x.tables +}} + +local consts = +{ + {{~ for c in consts ~}} + ---@class {{c.full_name}} + {{~ for item in c.items ~}} + ---@field public {{item.name}} {{item.type}} + {{~end~}} + ['{{c.full_name}}'] = { {{ for item in c.items }} {{item.name}}={{lua_const_value item.ctype item.value}}, {{end}} }; + {{~end~}} +} + +local enums = +{ + {{~ for c in enums ~}} + ---@class {{c.full_name}} + {{~ for item in c.items ~}} + ---@field public {{item.name}} int + {{~end~}} + ['{{c.full_name}}'] = { {{ for item in c.items }} {{item.name}}={{item.int_value}}, {{end}} }; + {{~end~}} +} + +local beans = {} +{{~ for bean in beans ~}} +---@class {{bean.full_name}} {{if bean.parent_def_type}}:{{bean.parent}} {{end}} +{{~ for field in bean.export_fields~}} +---@field public {{field.name}} {{lua_comment_type field.ctype}} +{{~end~}} +beans['{{bean.full_name}}'] = +{ +{{~ for field in bean.hierarchy_export_fields ~}} + { name='{{field.name}}', type='{{lua_comment_type field.ctype}}'}, +{{~end~}} +} + +{{~end~}} + +local tables = +{ +{{~for table in tables ~}} + {{~if table.is_two_key_map_table~}} + { name='{{table.name}}', file='{{table.output_data_file}}', mode='bmap', index1='{{table.index1}}', index2='{{table.index2}}', value_type='{{table.value_ttype.bean.full_name}}' }, + {{~else if table.is_map_table ~}} + { name='{{table.name}}', file='{{table.output_data_file}}', mode='map', index='{{table.index}}', value_type='{{table.value_ttype.bean.full_name}}' }, + {{~else~}} + { name='{{table.name}}', file='{{table.output_data_file}}', mode='one', value_type='{{table.value_ttype.bean.full_name}}'}, + {{end}} +{{~end~}} +} + +return { consts = consts, enums = enums, beans = beans, tables = tables } + +"); + return template.RenderCode(new { Consts = consts, Enums = enums, Beans = beans, Tables = tables }); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Generate/Python27JsonCodeRender.cs b/src/Luban.Job.Cfg/Source/Generate/Python27JsonCodeRender.cs new file mode 100644 index 0000000..b5c4bab --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Generate/Python27JsonCodeRender.cs @@ -0,0 +1,221 @@ +using Luban.Job.Cfg.Defs; +using Luban.Job.Common.Defs; +using Scriban; +using System; +using System.Collections.Generic; + +namespace Luban.Job.Cfg.Generate +{ + class Python27JsonCodeRender : CodeRenderBase + { + [ThreadStatic] + private static Template t_tsConstRender; + public override string Render(DefConst c) + { + var ctx = new TemplateContext(); + var env = new TTypeTemplateCommonExtends + { + ["x"] = c + }; + ctx.PushGlobal(env); + + + var template = t_tsConstRender ??= Template.Parse(@" + +class {{x.py_full_name}}: + {{~ for item in x.items ~}} + {{item.name}} = {{py_const_value item.ctype item.value}} + {{~end~}} + {{~if (x.items == empty)~}} + pass + {{~end~}} + +"); + var result = template.Render(ctx); + + return result; + } + + [ThreadStatic] + private static Template t_tsEnumRender; + public override string Render(DefEnum e) + { + var template = t_tsEnumRender ??= Template.Parse(@" +class {{py_full_name}}: + {{~ for item in items ~}} + {{item.name}} = {{item.value}} + {{~end~}} + {{~if (items == empty)~}} + pass + {{~end~}} +"); + var result = template.Render(e); + + return result; + } + + [ThreadStatic] + private static Template t_beanRender; + public override string Render(DefBean b) + { + var template = t_beanRender ??= Template.Parse(@" + +{{ + name = x.py_full_name + is_abstract_type = x.is_abstract_type + parent_def_type = x.parent_def_type + export_fields = x.export_fields + hierarchy_export_fields = x.hierarchy_export_fields +}} + +class {{name}} {{if parent_def_type}}({{parent_def_type.py_full_name}}){{end}}: +{{~if x.is_abstract_type~}} + _childrenTypes = None + + @staticmethod + def fromJson(_json_): + childrenTypes = {{name}}._childrenTypes + if not childrenTypes: + childrenTypes = {{name}}._childrenTypes = { + {{~ for child in x.hierarchy_not_abstract_children~}} + '{{child.name}}': {{child.py_full_name}}, + {{~end~}} + } + type = _json_['__type__'] + if type != None: + child = {{name}}._childrenTypes.get(type) + if child != None: + return child(_json_) + else: + raise Exception() + else: + return None +{{~end~}} + + def __init__(self, _json_): + {{~if parent_def_type~}} + {{parent_def_type.py_full_name}}.__init__(self, _json_) + {{~end~}} + {{~ for field in export_fields ~}} + {{~if !field.ctype.is_nullable~}} + if _json_['{{field.name}}'] == None: raise Exception() + {{~end~}} + {{py27_deserialize ('self.' + field.py_style_name) ('_json_[""' + field.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(@" +{{ + name = x.py_full_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 ~}} + def __init__(self, _json_): + self._dataListMap = {} + self._dataMapMap = {} + self._dataList = [] + + for _json2_ in _json_: + {{py27_deserialize '_v' '_json2_' value_type}} + self._dataList.append(_v) + _key = _v.{{x.index_field1.py_style_name}} + list = self._dataListMap.get(_key) + if not list: + list = [] + self._dataListMap[_key] = list + list.append(_v) + + map = self._dataMapMap.get(_key) + if not map: + map = {} + self._dataMapMap[_key] = map + map[_v.{{x.index_field2.py_style_name}}] = _v + + def getDataListMap(self) : return self._dataListMap + def getDataMapMap(self) : return self._dataMapMap + def getDataList(self) : return self._dataList + + def get(self, key1, key2) : + m1 = self._dataMapMap.get(key1) + return m1.get(key2) if m1 else None + + {{~else if x.is_map_table ~}} + + def __init__(self, _json_ ): + self._dataMap = {} + self._dataList = [] + + for _json2_ in _json_: + {{py27_deserialize '_v' '_json2_' value_type}} + self._dataList.append(_v) + self._dataMap[_v.{{x.index_field.py_style_name}}] = _v + + def getDataMap(self) : return self._dataMap + def getDataList(self) : return self._dataList + + def get(self, key) : return self._dataMap.get(key) + + {{~else~}} + + def __init__(self, _json_): + if (len(_json_) != 1): raise Exception('table mode=one, but size != 1') + {{py27_deserialize 'self._data' '_json_[0]' value_type}} + + def getData(self) : return self._data + + {{~ for field in value_type.bean.hierarchy_export_fields ~}} + def {{field.py_style_name}}(self) : return self._data.{{field.py_style_name}} + {{~end~}} + {{~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(@" +{{ + name = x.name + namespace = x.namespace + tables = x.tables +}} + +class {{name}}: + {{~ for table in tables ~}} + #def {{table.name}} : return self._{{table.name}} + {{~end~}} + + def __init__(self, loader): + {{~for table in tables ~}} + self.{{table.name}} = {{table.py_full_name}}(loader('{{table.json_output_data_file}}')); + {{~end~}} + +"); + var result = template.RenderCode(new + { + Name = name, + Namespace = module, + Tables = tables, + }); + + return result; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Generate/Python3JsonCodeRender.cs b/src/Luban.Job.Cfg/Source/Generate/Python3JsonCodeRender.cs new file mode 100644 index 0000000..f761961 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Generate/Python3JsonCodeRender.cs @@ -0,0 +1,221 @@ +using Luban.Job.Cfg.Defs; +using Luban.Job.Common.Defs; +using Scriban; +using System; +using System.Collections.Generic; + +namespace Luban.Job.Cfg.Generate +{ + class Python3JsonCodeRender : CodeRenderBase + { + [ThreadStatic] + private static Template t_tsConstRender; + public override string Render(DefConst c) + { + var ctx = new TemplateContext(); + var env = new TTypeTemplateCommonExtends + { + ["x"] = c + }; + ctx.PushGlobal(env); + + + var template = t_tsConstRender ??= Template.Parse(@" + +class {{x.py_full_name}}: + {{~ for item in x.items ~}} + {{item.name}} = {{py_const_value item.ctype item.value}} + {{~end~}} + {{~if (x.items == empty)~}} + pass + {{~end~}} + +"); + var result = template.Render(ctx); + + return result; + } + + [ThreadStatic] + private static Template t_tsEnumRender; + public override string Render(DefEnum e) + { + var template = t_tsEnumRender ??= Template.Parse(@" +class {{py_full_name}}(Enum): + {{~ for item in items ~}} + {{item.name}} = {{item.value}} + {{~end~}} + {{~if (items == empty)~}} + pass + {{~end~}} +"); + var result = template.Render(e); + + return result; + } + + [ThreadStatic] + private static Template t_beanRender; + public override string Render(DefBean b) + { + var template = t_beanRender ??= Template.Parse(@" + +{{ + name = x.py_full_name + is_abstract_type = x.is_abstract_type + parent_def_type = x.parent_def_type + export_fields = x.export_fields + hierarchy_export_fields = x.hierarchy_export_fields +}} + +class {{name}} {{if parent_def_type}}({{parent_def_type.py_full_name}}){{else if is_abstract_type}}(metaclass=abc.ABCMeta){{end}}: +{{~if x.is_abstract_type~}} + _childrenTypes = None + + @staticmethod + def fromJson(_json_): + childrenTypes = {{name}}._childrenTypes + if not childrenTypes: + childrenTypes = {{name}}._childrenTypes = { + {{~ for child in x.hierarchy_not_abstract_children~}} + '{{child.name}}': {{child.py_full_name}}, + {{~end~}} + } + type = _json_['__type__'] + if type != None: + child = {{name}}._childrenTypes.get(type) + if child != None: + return child(_json_) + else: + raise Exception() + else: + return None +{{~end~}} + + def __init__(self, _json_): + {{~if parent_def_type~}} + {{parent_def_type.py_full_name}}.__init__(self, _json_) + {{~end~}} + {{~ for field in export_fields ~}} + {{~if !field.ctype.is_nullable~}} + if _json_['{{field.name}}'] == None: raise Exception() + {{~end~}} + {{py3_deserialize ('self.' + field.py_style_name) ('_json_[""' + field.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(@" +{{ + name = x.py_full_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 ~}} + def __init__(self, _json_): + self._dataListMap = {} + self._dataMapMap = {} + self._dataList = [] + + for _json2_ in _json_: + {{py3_deserialize '_v' '_json2_' value_type}} + self._dataList.append(_v) + _key = _v.{{x.index_field1.py_style_name}} + list = self._dataListMap.get(_key) + if not list: + list = [] + self._dataListMap[_key] = list + list.append(_v) + + map = self._dataMapMap.get(_key) + if not map: + map = {} + self._dataMapMap[_key] = map + map[_v.{{x.index_field2.py_style_name}}] = _v + + def getDataListMap(self) : return self._dataListMap + def getDataMapMap(self) : return self._dataMapMap + def getDataList(self) : return self._dataList + + def get(self, key1, key2) : + m1 = self._dataMapMap.get(key1) + return m1.get(key2) if m1 else None + + {{~else if x.is_map_table ~}} + + def __init__(self, _json_ ): + self._dataMap = {} + self._dataList = [] + + for _json2_ in _json_: + {{py3_deserialize '_v' '_json2_' value_type}} + self._dataList.append(_v) + self._dataMap[_v.{{x.index_field.py_style_name}}] = _v + + def getDataMap(self) : return self._dataMap + def getDataList(self) : return self._dataList + + def get(self, key) : return self._dataMap.get(key) + + {{~else~}} + + def __init__(self, _json_): + if (len(_json_) != 1): raise Exception('table mode=one, but size != 1') + {{py3_deserialize 'self._data' '_json_[0]' value_type}} + + def getData(self) : return self._data + + {{~ for field in value_type.bean.hierarchy_export_fields ~}} + def {{field.py_style_name}}(self) : return self._data.{{field.py_style_name}} + {{~end~}} + {{~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(@" +{{ + name = x.name + namespace = x.namespace + tables = x.tables +}} + +class {{name}}: + {{~ for table in tables ~}} + #def {{table.name}} : return self._{{table.name}} + {{~end~}} + + def __init__(self, loader): + {{~for table in tables ~}} + self.{{table.name}} = {{table.py_full_name}}(loader('{{table.json_output_data_file}}')); + {{~end~}} + +"); + var result = template.RenderCode(new + { + Name = name, + Namespace = module, + Tables = tables, + }); + + return result; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Generate/TypeScriptJsonCodeRender.cs b/src/Luban.Job.Cfg/Source/Generate/TypeScriptJsonCodeRender.cs new file mode 100644 index 0000000..a7143f4 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Generate/TypeScriptJsonCodeRender.cs @@ -0,0 +1,281 @@ +using Luban.Job.Cfg.Defs; +using Luban.Job.Common.Defs; +using Scriban; +using System; +using System.Collections.Generic; + +namespace Luban.Job.Cfg.Generate +{ + class TypeScriptJsonCodeRender : CodeRenderBase + { + [ThreadStatic] + private static Template t_tsConstRender; + public override string Render(DefConst c) + { + var ctx = new TemplateContext(); + var env = new TTypeTemplateCommonExtends + { + ["x"] = c + }; + ctx.PushGlobal(env); + + + var template = t_tsConstRender ??= Template.Parse(@" +namespace {{x.namespace}} { +export class {{x.name}} { + {{~ for item in x.items ~}} + static {{item.name}} : {{ts_define_type item.ctype}} = {{ts_const_value item.ctype item.value}}; + {{~end~}} +} +} + +"); + var result = template.Render(ctx); + + return result; + } + + [ThreadStatic] + private static Template t_tsEnumRender; + public override string Render(DefEnum e) + { + var template = t_tsEnumRender ??= Template.Parse(@" +namespace {{namespace}} { +export enum {{name}} { + {{- for item in items }} + {{item.name}} = {{item.value}}, + {{-end}} +} +} + +"); + var result = template.Render(e); + + return result; + } + + [ThreadStatic] + private static Template t_beanRender; + public override string Render(DefBean b) + { + var template = t_beanRender ??= Template.Parse(@" + +{{ + 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}} { + +export {{if x.is_abstract_type}} abstract {{end}} class {{name}} {{if parent_def_type}} extends {{x.parent}}{{end}} { +{{~if x.is_abstract_type~}} + static deserialize(_json_ : any) : {{name}} { + switch (_json_.__type__) + { + case null : return null; + {{~ for child in x.hierarchy_not_abstract_children~}} + case '{{child.name}}': return new {{child.full_name}}(_json_); + {{~end~}} + default: throw new Error(); + } + } +{{~end~}} + + constructor(_json_ : any) { + {{~if parent_def_type~}} + super(_json_); + {{~end~}} + {{~ for field in export_fields ~}} + {{~if !field.ctype.is_nullable~}} + if (_json_.{{field.name}} == null) { throw new Error(); } + {{~end~}} + {{ts_deserialize ('this.' + field.ts_style_name) ( '_json_.' + field.name) field.ctype}} + {{~end~}} + } + + {{~ for field in export_fields ~}} + {{field.ts_style_name}} : {{ts_define_type field.ctype}}; + {{~if field.gen_ref~}} + {{field.ts_ref_validator_define}} + {{~end~}} + {{~end~}} + + resolve(_tables : Map<string, any>) : void { + {{~if parent_def_type~}} + super.resolve(_tables); + {{~end~}} + {{~ for field in export_fields ~}} + {{~if field.gen_ref~}} + {{ts_ref_validator_resolve field}} + {{~else if field.has_recursive_ref~}} + {{ts_recursive_resolve field '_tables'}} + {{~end~}} + {{~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(@" + {{ + 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}} { +export class {{name}}{ + {{~ if x.is_two_key_map_table ~}} + private _dataListMap : Map<{{ts_define_type key_type1}}, {{ts_define_type value_type}}[]>; + private _dataMapMap : Map<{{ts_define_type key_type1}}, Map<{{ts_define_type key_type2}}, {{ts_define_type value_type}}>>; + private _dataList : {{ts_define_type value_type}}[]; + + constructor(_json_ : any) { + this._dataListMap = new Map<{{ts_define_type key_type1}}, {{ts_define_type value_type}}[]>(); + this._dataMapMap = new Map<{{ts_define_type key_type1}}, Map<{{ts_define_type key_type2}}, {{ts_define_type value_type}}>>(); + this._dataList = []; + + for(var _json2_ of _json_) { + let _v : {{ts_define_type value_type}}; + {{ts_deserialize '_v' '_json2_' value_type}} + this._dataList.push(_v); + var _key = _v.{{x.index_field1.ts_style_name}}; + let list : {{ts_define_type value_type}}[] = this._dataListMap.get(_key); + if (list == null) { + list = []; + this._dataListMap.set(_key, list); + } + list.push(_v); + + let map : Map<{{ts_define_type key_type2}}, {{ts_define_type value_type}}> = this._dataMapMap.get(_key); + if (map == null) { + map = new Map<{{ts_define_type key_type2}}, {{ts_define_type value_type}}>(); + this._dataMapMap.set(_key, map); + } + map.set(_v.{{x.index_field2.ts_style_name}}, _v); + } + } + + getDataListMap() : Map<{{ts_define_type key_type1}}, {{ts_define_type value_type}}[]> { return this._dataListMap; } + getDataMapMap() : Map<{{ts_define_type key_type1}}, Map<{{ts_define_type key_type2}}, {{ts_define_type value_type}}>> { return this._dataMapMap; } + getDataList() : {{ts_define_type value_type}}[] { return this._dataList; } + + get(key1 : {{ts_define_type key_type1}}, key2 : {{ts_define_type key_type2}}) : {{ts_define_type value_type}} { return this._dataMapMap.get(key1).get(key2); } + + resolve(_tables : Map<string, any>) : void { + for(var v of this._dataList) { + v.resolve(_tables); + } + } + {{~else if x.is_map_table ~}} + private _dataMap : Map<{{ts_define_type key_type}}, {{ts_define_type value_type}}>; + private _dataList : {{ts_define_type value_type}}[]; + + constructor(_json_ : any) { + this._dataMap = new Map<{{ts_define_type key_type}}, {{ts_define_type value_type}}>(); + this._dataList = []; + + for(var _json2_ of _json_) { + let _v : {{ts_define_type value_type}}; + {{ts_deserialize '_v' '_json2_' value_type}} + this._dataList.push(_v); + this._dataMap.set(_v.{{x.index_field.ts_style_name}}, _v); + } + } + + getDataMap() : Map<{{ts_define_type key_type}}, {{ts_define_type value_type}}> { return this._dataMap; } + getDataList() : {{ts_define_type value_type}}[] { return this._dataList; } + + get(key : {{ts_define_type key_type}}) : {{ts_define_type value_type}} { return this._dataMap.get(key); } + + resolve(_tables : Map<string, any>) : void { + for(var v of this._dataList) { + v.resolve(_tables); + } + } + + {{~else~}} + + private _data : {{ts_define_type value_type}}; + + constructor(_json_ : any) { + if (_json_.length != 1) throw new Error('table mode=one, but size != 1'); + {{ts_deserialize 'this._data' '_json_[0]' value_type}} + } + + getData() : {{ts_define_type value_type}} { return this._data; } + + {{~ for field in value_type.bean.hierarchy_export_fields ~}} + get {{field.ts_style_name}}() : {{ts_define_type field.ctype}} { return this._data.{{field.ts_style_name}}; } + {{~end~}} + + resolve(_tables : Map<string, any>) : void { + this._data.resolve(_tables); + } + + {{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(@" +{{ + name = x.name + namespace = x.namespace + tables = x.tables + +}} + +type JsonLoader = (file : string) => any + +export class {{name}} { + {{~ for table in tables ~}} + private _{{table.name}} : {{table.full_name}}; + get {{table.name}}() : {{table.full_name}} { return this._{{table.name}};} + {{~end~}} + + constructor(loader : JsonLoader) { + let tables = new Map<string, any>(); + {{~for table in tables ~}} + this._{{table.name}} = new {{table.full_name}}(loader('{{table.json_output_data_file}}')); + tables.set('{{table.full_name}}', this._{{table.name}}); + {{~end~}} + + {{~ for table in tables ~}} + this._{{table.name}}.resolve(tables); + {{~end~}} + } +} + +"); + var result = template.RenderCode(new + { + Name = name, + Namespace = module, + Tables = tables, + }); + + return result; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Generate/UE4BpCppRender.cs b/src/Luban.Job.Cfg/Source/Generate/UE4BpCppRender.cs new file mode 100644 index 0000000..6b3b775 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Generate/UE4BpCppRender.cs @@ -0,0 +1,91 @@ +using Luban.Job.Cfg.Defs; +using Luban.Job.Common.Defs; +using Scriban; +using System; + +namespace Luban.Job.Cfg.Generate +{ + class UE4BpCppRender + { + public string RenderAny(object o) + { + switch (o) + { + case DefEnum e: return Render(e); + case DefBean b: return Render(b); + //case CTable r: return Render(r); + default: throw new Exception($"unknown render type:{o}"); + } + } + + [ThreadStatic] + private static Template t_enumRender; + public string Render(DefEnum e) + { + // ue 不允许 UEnum 为这 + // ue 强制枚举underling type 为 uint8, 意味着不能超过255 + var template = t_enumRender ??= Template.Parse(@" +#pragma once +#include ""CoreMinimal.h"" + +#include ""{{ue_bp_header_file_name_without_suffix}}.generated.h"" + +UENUM(BlueprintType) +enum class {{ue_bp_full_name}} : uint8 +{ + {{if !contains_value_equal0_item}} + __DEFAULT__ = 0, + {{end}} + {{if contains_any_ue_enum_compatible_item}} + {{- for item in items }} + {{if item.int_value >= 256}}//{{end}} {{item.name}} = {{item.value}} UMETA(DisplayName = ""{{item.alias_or_name}}""), + {{-end}} + {{else}} + DUMMY UMETA(DisplayName = ""DUMMY""), + {{end}} +}; + +"); + var result = template.Render(e); + + return result; + } + + [ThreadStatic] + private static Template t_beanRender; + public string Render(DefBean b) + { + var template = t_beanRender ??= Template.Parse(@" +#pragma once +#include ""CoreMinimal.h"" +#include ""UCfgObj.h"" + + +{{ue_bp_includes}} + +#include ""{{ue_bp_header_file_name_without_suffix}}.generated.h"" + +UCLASS(BlueprintType) +class X6PROTO_API {{ue_bp_full_name}} : public {{if parent_def_type}} {{parent_def_type.ue_bp_full_name}} {{else}} UCfgObj {{end}} +{ + GENERATED_BODY() + +public: + + + {{- for field in export_fields }} + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (DisplayName = ""{{field.name}}"")) + {{field.ctype.ue_bp_cpp_define_type}} {{field.name}}; + {{-end}} +}; + + +"); + var result = template.Render(b); + + return result; + } + + + } +} diff --git a/src/Luban.Job.Cfg/Source/Generate/UE4EditorCppRender.cs b/src/Luban.Job.Cfg/Source/Generate/UE4EditorCppRender.cs new file mode 100644 index 0000000..285316e --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Generate/UE4EditorCppRender.cs @@ -0,0 +1,233 @@ +using Luban.Job.Cfg.Defs; +using Luban.Job.Common.Defs; +using Scriban; +using System; +using System.Collections.Generic; + +namespace Luban.Job.Cfg.Generate +{ + class UE4EditorCppRender + { + public string RenderAny(object o) + { + switch (o) + { + case DefEnum e: return Render(e); + case DefBean b: return Render(b); + //case CTable r: return Render(r); + default: throw new Exception($"unknown render type:{o}"); + } + } + + [ThreadStatic] + private static Template t_enumRender; + public string Render(DefEnum e) + { + var template = t_enumRender ??= Template.Parse(@" +#pragma once +#include ""CoreMinimal.h"" + +namespace editor +{ + +{{cpp_namespace_begin}} + + enum class {{ue_fname}} + { + {{- for item in items }} + {{item.name}} = {{item.value}}, + {{-end}} + }; + + bool X6PROTOEDITOR_API {{ue_fname}}ToString({{ue_fname}} value, FString& s); + bool X6PROTOEDITOR_API {{ue_fname}}FromString(const FString& s, {{ue_fname}}& x); + +{{cpp_namespace_end}} + +} +"); + var result = template.Render(e); + + return result; + } + + [ThreadStatic] + private static Template t_beanRender; + public string Render(DefBean b) + { + var template = t_beanRender ??= Template.Parse(@" +#pragma once +#include ""CoreMinimal.h"" +#include ""FCfgObj.h"" + +{{editor_cpp_includes}} + +namespace editor +{ + +{{cpp_namespace_begin}} + +struct X6PROTOEDITOR_API {{ue_fname}} : public {{if parent_def_type}} {{parent_def_type.ue_fname}}{{else}}FCfgObj{{end}} +{ + {{- for field in fields }} + {{field.ctype.editor_ue_cpp_define_type}} {{field.name}}; + {{-end}} + +{{if !is_abstract_type}} + bool Load(FJsonObject* _json) override; + bool Save(FJsonObject*& result) override; +{{end}} + + static bool Create(FJsonObject* _json, {{ue_fname}}*& result); +}; + + +{{cpp_namespace_end}} + +} + +"); + var result = template.Render(b); + + return result; + } + + + private class Stub + { + public List<DefTypeBase> Types { get; set; } + + public string Includes + { + get + { + var includeTypes = new HashSet<DefTypeBase>(Types); + + foreach (var type in Types) + { + if (type is DefBean bean) + { + foreach (DefBean c in bean.HierarchyNotAbstractChildren) + { + includeTypes.Add(c); + } + } + } + //return string.Join('\n', includeTypes.Select(im => $"#include \"{ im.UeEditorHeaderFileName}\"")); + throw new NotImplementedException(); + } + } + } + + [ThreadStatic] + private static Template t_stubRender; + public string RenderStub(List<DefTypeBase> types) + { + var template = t_stubRender ??= Template.Parse(@" +#include ""JsonUtil.h"" + +{{includes}} + +namespace editor +{ + +{{for type in types}} +{{type.cpp_namespace_begin}} +{{if type.is_bean}} +{{if type.is_abstract_type}} + bool {{type.ue_fname}}::Create(FJsonObject* _json, {{type.ue_fname}}*& result) + { + FString type; + if (_json->TryGetStringField(FString(""__type__""), type)) + { + {{-for child in type.hierarchy_not_abstract_children}} + if (type == ""{{child.name}}"") + { + result = new {{child.ue_fname}}(); + } else + {{-end}} + { + result = nullptr; + return false; + } + if (!result->Load(_json)) + { + delete result; + return false; + } + return true; + } + else + { + result = nullptr; + return false; + } + } +{{else}} + bool {{type.ue_fname}}::Create(FJsonObject* _json, {{type.ue_fname}}*& result) + { + result = new {{type.ue_fname}}(); + if (!result->Load(_json)) + { + delete result; + return false; + } + return true; + } + + + bool {{type.ue_fname}}::Save(FJsonObject*& result) + { + auto _json = new FJsonObject(); + _json->SetStringField(""__type__"", ""{{type.name}}""); + +{{for field in type.hierarchy_fields}} + {{field.editor_ue_cpp_save}} +{{end}} + result = _json; + return true; + } + + bool {{type.ue_fname}}::Load(FJsonObject* _json) + { +{{for field in type.hierarchy_fields}} + {{field.editor_ue_cpp_load}} +{{end}} + return true; + } +{{end}} +{{else}} + +bool {{type.ue_fname}}ToString({{type.ue_fname}} value, FString& s) +{ + {{- for item in type.items }} + if (value == {{type.ue_fname}}::{{item.name}}) { s = ""{{item.name}}""; return true; } + {{-end}} + return false; +} +bool {{type.ue_fname}}FromString(const FString& s, {{type.ue_fname}}& value) +{ + {{- for item in type.items }} + if (s == ""{{item.name}}"") + { + value = {{type.ue_fname}}::{{item.name}}; + return true; + } + {{-end}} + return false; +} + +{{end}} +{{type.cpp_namespace_end}} +{{end}} +} +"); + var result = template.Render(new Stub + { + Types = types, + }); + + return result; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/JobController.cs b/src/Luban.Job.Cfg/Source/JobController.cs new file mode 100644 index 0000000..8fc9399 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/JobController.cs @@ -0,0 +1,1112 @@ +using Bright.Serialization; +using Bright.Time; +using CommandLine; +using Luban.Common.Protos; +using Luban.Common.Utils; +using Luban.Config.Common.RawDefs; +using Luban.Job.Cfg.Cache; +using Luban.Job.Cfg.Datas; +using Luban.Job.Cfg.DataSources; +using Luban.Job.Cfg.DataVisitors; +using Luban.Job.Cfg.Defs; +using Luban.Job.Cfg.Generate; +using Luban.Job.Cfg.RawDefs; +using Luban.Job.Cfg.Utils; +using Luban.Job.Common; +using Luban.Job.Common.Defs; +using Luban.Job.Common.Utils; +using Luban.Server.Common; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json; +using System.Threading.Tasks; +using FileInfo = Luban.Common.Protos.FileInfo; + +namespace Luban.Job.Cfg +{ + [Controller("cfg")] + public class JobController : IJobController + { + private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger(); + + class GenCfgArgs + { + [Option('d', "define_file", Required = true, HelpText = "define file")] + public string DefineFile { get; set; } + + [Option("input_data_dir", Required = true, HelpText = "input data dir")] + public string InputDataDir { get; set; } + + [Option('v', "validate_root_dir", Required = false, HelpText = "validate root directory")] + public string ValidateRootDir { get; set; } + + [Option("output_code_dir", Required = false, HelpText = "output code directory")] + public string OutputCodeDir { get; set; } + + [Option("output_data_dir", Required = true, HelpText = "output data directory")] + public string OutputDataDir { get; set; } + + [Option("gen_types", Required = true, HelpText = "cs_bin,cs_json,lua_bin,go_bin,cpp_ue_editor,cs_unity_editor can be multi")] + public string GenType { get; set; } + + [Option('s', "service", Required = true, HelpText = "service")] + public string Service { get; set; } + + [Option("export_test_data", Required = false, HelpText = "export test data")] + public bool ExportTestData { get; set; } = false; + + [Option('t', "timezone", Required = false, HelpText = "timezone")] + public string TimeZone { get; set; } + } + + private async Task LoadCfgDataAsync(RemoteAgent agent, DefAssembly ass, string dataDir, bool exportTestData) + { + var ctx = agent; + List<DefTable> exportTables = ass.Types.Values.Where(t => t is DefTable ct && ct.NeedExport).Select(t => (DefTable)t).ToList(); + var genDataTasks = new List<Task>(); + var outputDataFiles = new ConcurrentBag<FileInfo>(); + long genDataStartTime = TimeUtil.NowMillis; + + foreach (DefTable c in exportTables) + { + genDataTasks.Add(Task.Run(async () => + { + long beginTime = TimeUtil.NowMillis; + await LoadTableAsync(agent, c, dataDir, exportTestData); + long endTime = TimeUtil.NowMillis; + if (endTime - beginTime > 100) + { + ctx.Info("====== load {0} cost {1} ms ======", c.FullName, (endTime - beginTime)); + } + })); + } + await Task.WhenAll(genDataTasks.ToArray()); + } + + private ICodeRender CreateCodeRender(string genType) + { + switch (genType) + { + case "code_cs_bin": return new CsBinCodeRender(); + case "code_cs_json": return new CsJsonCodeRender(); + case "code_java_bin": return new JavaBinCodeRender(); + case "code_cpp_bin": return new CppBinCodeRender(); + default: throw new ArgumentException($"not support gen type:{genType}"); + } + } + + private ELanguage GetLanguage(string genType) + { + switch (genType) + { + case "code_cs_bin": + case "code_cs_json": return ELanguage.CS; + case "code_java_bin": return ELanguage.JAVA; + case "code_cpp_bin": return ELanguage.CPP; + case "code_go_bin": return ELanguage.GO; + case "code_lua_bin": return ELanguage.LUA; + default: throw new ArgumentException($"not support output data type:{genType}"); + } + } + + + private bool TryParseArg(List<string> args, out GenCfgArgs result, out string errMsg) + { + var helpWriter = new StringWriter(); + var parser = new Parser(ps => + { + ps.HelpWriter = helpWriter; + }); ; + var parseResult = parser.ParseArguments<GenCfgArgs>(args); + if (parseResult.Tag == ParserResultType.NotParsed) + { + errMsg = helpWriter.ToString(); + result = null; + return false; + } + else + { + result = (parseResult as Parsed<GenCfgArgs>).Value; + errMsg = null; + + string inputDataDir = result.InputDataDir; + string outputCodeDir = result.OutputCodeDir; + string outputDataDir = result.OutputDataDir; + + var genTypes = result.GenType.Split(',').Select(s => s.Trim()).ToList(); + + if (genTypes.Any(t => t.StartsWith("code_", StringComparison.Ordinal)) && string.IsNullOrWhiteSpace(outputCodeDir)) + { + errMsg = "--outputcodedir missing"; + return false; + } + else if (genTypes.Any(t => t.StartsWith("data_", StringComparison.Ordinal))) + { + if (string.IsNullOrWhiteSpace(inputDataDir)) + { + errMsg = "--inputdatadir missing"; + return false; + } + if (string.IsNullOrWhiteSpace(outputDataDir)) + { + errMsg = "--outputdatadir missing"; + return false; + } + } + + return true; + } + } + + public async Task GenAsync(RemoteAgent agent, GenJob rpc) + { + var res = new GenJobRes() + { + ErrCode = Luban.Common.EErrorCode.OK, + ErrMsg = "succ", + FileGroups = new List<FileGroup>(), + }; + + if (!TryParseArg(rpc.Arg.JobArguments, out GenCfgArgs args, out string errMsg)) + { + res.ErrCode = Luban.Common.EErrorCode.JOB_ARGUMENT_ERROR; + res.ErrMsg = errMsg; + agent.Session.ReplyRpc<GenJob, GenJobArg, GenJobRes>(rpc, res); + return; + } + + var timer = new ProfileTimer(); + timer.StartPhase("= gen_all ="); + try + { + string inputDataDir = args.InputDataDir; + string outputCodeDir = args.OutputCodeDir; + string outputDataDir = args.OutputDataDir; + + var genTypes = args.GenType.Split(',').Select(s => s.Trim()).ToList(); + + timer.StartPhase("build defines"); + var loader = new CfgDefLoader(agent); + await loader.LoadAsync(args.DefineFile); + timer.EndPhaseAndLog(); + + var rawDefines = loader.BuildDefines(); + + TimeZoneInfo timeZoneInfo = string.IsNullOrEmpty(args.TimeZone) ? TimeZoneInfo.Local : TimeZoneInfo.FindSystemTimeZoneById(args.TimeZone); + + var ass = new DefAssembly(timeZoneInfo); + + ass.Load(args.Service, rawDefines, agent); + + var targetService = ass.CfgTargetService; + + + List<DefTable> exportTables = ass.GetExportTables(); + List<DefTypeBase> exportTypes = ass.GetExportTypes(); + + bool hasLoadCfgData = false; + + + async Task CheckLoadCfgDataAsync() + { + if (!hasLoadCfgData) + { + hasLoadCfgData = true; + var timer = new ProfileTimer(); + timer.StartPhase("load config data"); + await LoadCfgDataAsync(agent, ass, args.InputDataDir, args.ExportTestData); + timer.EndPhaseAndLog(); + + timer.StartPhase("validate"); + var validateCtx = new ValidatorContext(ass, args.ValidateRootDir); + await validateCtx.ValidateTables(exportTables); + timer.EndPhaseAndLog(); + } + } + + var tasks = new List<Task>(); + + var genCodeFiles = new ConcurrentBag<FileInfo>(); + var genDataFiles = new ConcurrentBag<FileInfo>(); + + foreach (var genType in genTypes) + { + switch (genType) + { + case "code_cs_bin": + case "code_cs_json": + + case "code_java_bin": + { + ICodeRender render = CreateCodeRender(genType); + ELanguage lan = GetLanguage(genType); + + foreach (var c in exportTypes) + { + tasks.Add(Task.Run(() => + { + var content = FileHeaderUtil.ConcatAutoGenerationHeader(render.RenderAny(c), lan); + var file = RenderFileUtil.GetDefTypePath(c.FullName, lan); + var md5 = CacheFileUtil.GenMd5AndAddCache(file, content); + genCodeFiles.Add(new FileInfo() { FilePath = file, MD5 = md5 }); + })); + } + + tasks.Add(Task.Run(() => + { + var module = ass.TopModule; + var name = targetService.Manager; + var content = FileHeaderUtil.ConcatAutoGenerationHeader(render.RenderService(name, module, exportTables), lan); + var file = RenderFileUtil.GetDefTypePath(name, lan); + var md5 = CacheFileUtil.GenMd5AndAddCache(file, content); + genCodeFiles.Add(new FileInfo() { FilePath = file, MD5 = md5 }); + })); + + break; + } + case "code_lua_bin": + { + tasks.Add(Task.Run(() => + { + var render = new LuaRender(); + var content = FileHeaderUtil.ConcatAutoGenerationHeader(render.RenderAll(ass.Types.Values.ToList()), ELanguage.LUA); + var file = "Types.lua"; + var md5 = CacheFileUtil.GenMd5AndAddCache(file, content); + genCodeFiles.Add(new FileInfo() { FilePath = file, MD5 = md5 }); + })); + break; + } + case "code_go_bin": + { + var render = new GoCodeRender(); + foreach (var c in exportTypes) + { + tasks.Add(Task.Run(() => + { + var content = FileHeaderUtil.ConcatAutoGenerationHeader(render.RenderAny(c), ELanguage.GO); + var file = RenderFileUtil.GetDefTypePath(c.FullName, ELanguage.GO); + var md5 = CacheFileUtil.GenMd5AndAddCache(file, content); + genCodeFiles.Add(new FileInfo() { FilePath = file, MD5 = md5 }); + })); + } + + tasks.Add(Task.Run(() => + { + var module = ass.TopModule; + var name = targetService.Manager; + var content = FileHeaderUtil.ConcatAutoGenerationHeader(render.RenderService(name, module, exportTables), ELanguage.GO); + var file = RenderFileUtil.GetDefTypePath(name, ELanguage.GO); + var md5 = CacheFileUtil.GenMd5AndAddCache(file, content); + genCodeFiles.Add(new FileInfo() { FilePath = file, MD5 = md5 }); + })); + break; + } + case "code_cpp_bin": + { + var render = new CppBinCodeRender(); + + + // 将所有 头文件定义 生成到一个文件 + // 按照 const,enum,bean,table, service 的顺序生成 + + tasks.Add(Task.Run(() => + { + var headerFileContent = new List<string> + { + @$" +#pragma once +#include <functional> + +#include ""bright/serialization/ByteBuf.h"" +#include ""bright/CfgBean.hpp"" + +using ByteBuf = bright::serialization::ByteBuf; + +namespace {ass.TopModule} +{{ + +" + }; + + foreach (var type in exportTypes) + { + if (type is DefEnum e) + { + headerFileContent.Add(render.Render(e)); + } + } + + foreach (var type in exportTypes) + { + if (type is DefConst c) + { + headerFileContent.Add(render.Render(c)); + } + } + + foreach (var type in exportTypes) + { + if (type is DefBean e) + { + headerFileContent.Add(render.RenderForwardDefine(e)); + } + } + + foreach (var type in exportTypes) + { + if (type is DefBean e) + { + headerFileContent.Add(render.Render(e)); + } + } + + foreach (var type in exportTables) + { + headerFileContent.Add(render.Render(type)); + } + + headerFileContent.Add(render.RenderService("Tables", ass.TopModule, exportTables)); + + headerFileContent.Add("}"); // end of topmodule + + var content = FileHeaderUtil.ConcatAutoGenerationHeader(string.Join('\n', headerFileContent), ELanguage.CPP); + var file = "gen_types.h"; + var md5 = CacheFileUtil.GenMd5AndAddCache(file, content); + genCodeFiles.Add(new FileInfo() { FilePath = file, MD5 = md5 }); + })); + + var beanTypes = exportTypes.Where(c => c is DefBean).ToList(); + + int TYPE_PER_STUB_FILE = 100; + + for (int i = 0, n = (beanTypes.Count + TYPE_PER_STUB_FILE - 1) / TYPE_PER_STUB_FILE; i < n; i++) + { + int index = i; + tasks.Add(Task.Run(() => + { + int startIndex = index * TYPE_PER_STUB_FILE; + var content = FileHeaderUtil.ConcatAutoGenerationHeader( + render.RenderStub(ass.TopModule, beanTypes.GetRange(startIndex, Math.Min(TYPE_PER_STUB_FILE, beanTypes.Count - startIndex))), + ELanguage.CPP); + var file = $"gen_stub_{index}.cpp"; + var md5 = CacheFileUtil.GenMd5AndAddCache(file, content); + genCodeFiles.Add(new FileInfo() { FilePath = file, MD5 = md5 }); + })); + } + break; + } + + case "code_typescript_json": + { + var render = new TypeScriptJsonCodeRender(); + tasks.Add(Task.Run(() => + { + var fileContent = new List<string> + { + @$" +export namespace {ass.TopModule} {{ +", + + @" +export class Vector2 { + x: number; + y: number; + constructor(x: number, y: number) { + this.x = x; + this.y = y; + } + + static fromJson(_json_: any): Vector2 { + let x = _json_['x']; + let y = _json_['y']; + if (x == null || y == null) { + throw new Error(); + } + return new Vector2(x, y); + } + } + + + export class Vector3 { + x: number; + y: number; + z: number; + constructor(x: number, y: number, z: number) { + this.x = x; + this.y = y; + this.z = z; + } + + static fromJson(_json_: any): Vector3 { + let x = _json_['x']; + let y = _json_['y']; + let z = _json_['z']; + if (x == null || y == null || z == null) { + throw new Error(); + } + return new Vector3(x, y, z); + } + } + + export class Vector4 { + x: number; + y: number; + z: number; + w: number; + constructor(x: number, y: number, z: number, w: number) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + static fromJson(_json_: any): Vector4 { + let x = _json_['x']; + let y = _json_['y']; + let z = _json_['z']; + let w = _json_['w']; + if (x == null || y == null || z == null || w == null) { + throw new Error(); + } + return new Vector4(x, y, z, w); + } + } + +" + }; + + foreach (var type in exportTypes) + { + if (type is DefEnum e) + { + fileContent.Add(render.Render(e)); + } + } + + foreach (var type in exportTypes) + { + if (type is DefConst c) + { + fileContent.Add(render.Render(c)); + } + } + + foreach (var type in exportTypes) + { + if (type is DefBean e) + { + fileContent.Add(render.Render(e)); + } + } + + foreach (var type in exportTables) + { + fileContent.Add(render.Render(type)); + } + + fileContent.Add(render.RenderService("Tables", ass.TopModule, exportTables)); + + fileContent.Add("}"); // end of topmodule + + var content = FileHeaderUtil.ConcatAutoGenerationHeader(string.Join('\n', fileContent), ELanguage.TYPESCRIPT); + var file = "Types.ts"; + var md5 = CacheFileUtil.GenMd5AndAddCache(file, content); + genCodeFiles.Add(new FileInfo() { FilePath = file, MD5 = md5 }); + })); + break; + } + + case "code_python27_json": + { + var render = new Python27JsonCodeRender(); + + tasks.Add(Task.Run(() => + { + var fileContent = new List<string> + { + @" +class Vector2: + def __init__(self, x, y): + self.x = x + self.y = y + self.a = Vector4(1,2,3,4) + def __str__(self): + return '{%g,%g}' % (self.x, self.y) + + @staticmethod + def fromJson(_json_): + x = _json_['x'] + y = _json_['y'] + if (x == None or y == None): + raise Exception() + return Vector2(x, y) + + +class Vector3: + def __init__(self, x, y, z): + self.x = x + self.y = y + self.z = z + def __str__(self): + return '{%f,%f,%f}' % (self.x, self.y, self.z) + @staticmethod + def fromJson(_json_): + x = _json_['x'] + y = _json_['y'] + z = _json_['z'] + if (x == None or y == None or z == None): + raise Exception() + return Vector3(x, y, z) + +class Vector4: + def __init__(self, x, y, z, w): + self.x = x + self.y = y + self.z = z + self.w = w + def __str__(self): + return '{%g,%g,%g,%g}' % (self.x, self.y, self.z, self.w) + + @staticmethod + def fromJson(_json_): + x = _json_['x'] + y = _json_['y'] + z = _json_['z'] + w = _json_['w'] + if (x == None or y == None or z == None or w == None): + raise Exception() + return Vector4(x, y, z, w) + +" + }; + + foreach (var type in exportTypes) + { + if (type is DefEnum e) + { + fileContent.Add(render.Render(e)); + } + } + + foreach (var type in exportTypes) + { + if (type is DefConst c) + { + fileContent.Add(render.Render(c)); + } + } + + foreach (var type in exportTypes) + { + if (type is DefBean e) + { + fileContent.Add(render.Render(e)); + } + } + + foreach (var type in exportTables) + { + fileContent.Add(render.Render(type)); + } + + fileContent.Add(render.RenderService("Tables", ass.TopModule, exportTables)); + + var content = FileHeaderUtil.ConcatAutoGenerationHeader(string.Join('\n', fileContent), ELanguage.PYTHON); + var file = "Types.py"; + var md5 = CacheFileUtil.GenMd5AndAddCache(file, content); + genCodeFiles.Add(new FileInfo() { FilePath = file, MD5 = md5 }); + + + { + var moduleInitContent = ""; + var initFile = "__init__.py"; + + var initMd5 = CacheFileUtil.GenMd5AndAddCache(initFile, moduleInitContent); + genCodeFiles.Add(new FileInfo() { FilePath = initFile, MD5 = initMd5 }); + } + })); + break; + } + + case "code_python3_json": + { + var render = new Python3JsonCodeRender(); + + tasks.Add(Task.Run(() => + { + var fileContent = new List<string> + { + @" +from enum import Enum +import abc + +class Vector2: + def __init__(self, x, y): + self.x = x + self.y = y + self.a = Vector4(1,2,3,4) + def __str__(self): + return '{%g,%g}' % (self.x, self.y) + + @staticmethod + def fromJson(_json_): + x = _json_['x'] + y = _json_['y'] + if (x == None or y == None): + raise Exception() + return Vector2(x, y) + + +class Vector3: + def __init__(self, x, y, z): + self.x = x + self.y = y + self.z = z + def __str__(self): + return '{%f,%f,%f}' % (self.x, self.y, self.z) + @staticmethod + def fromJson(_json_): + x = _json_['x'] + y = _json_['y'] + z = _json_['z'] + if (x == None or y == None or z == None): + raise Exception() + return Vector3(x, y, z) + +class Vector4: + def __init__(self, x, y, z, w): + self.x = x + self.y = y + self.z = z + self.w = w + def __str__(self): + return '{%g,%g,%g,%g}' % (self.x, self.y, self.z, self.w) + + @staticmethod + def fromJson(_json_): + x = _json_['x'] + y = _json_['y'] + z = _json_['z'] + w = _json_['w'] + if (x == None or y == None or z == None or w == None): + raise Exception() + return Vector4(x, y, z, w) + +" + }; + + foreach (var type in exportTypes) + { + if (type is DefEnum e) + { + fileContent.Add(render.Render(e)); + } + } + + foreach (var type in exportTypes) + { + if (type is DefConst c) + { + fileContent.Add(render.Render(c)); + } + } + + foreach (var type in exportTypes) + { + if (type is DefBean e) + { + fileContent.Add(render.Render(e)); + } + } + + foreach (var type in exportTables) + { + fileContent.Add(render.Render(type)); + } + + fileContent.Add(render.RenderService("Tables", ass.TopModule, exportTables)); + + var content = FileHeaderUtil.ConcatAutoGenerationHeader(string.Join('\n', fileContent), ELanguage.PYTHON); + var file = "Types.py"; + var md5 = CacheFileUtil.GenMd5AndAddCache(file, content); + genCodeFiles.Add(new FileInfo() { FilePath = file, MD5 = md5 }); + })); + break; + } + + case "code_cpp_ue_editor": + { + var render = new UE4EditorCppRender(); + + var renderTypes = ass.Types.Values.Where(c => c is DefEnum || c is DefBean).ToList(); + + foreach (var c in renderTypes) + { + tasks.Add(Task.Run(() => + { + var content = FileHeaderUtil.ConcatAutoGenerationHeader(render.RenderAny(c), ELanguage.CPP); + var file = "editor_" + RenderFileUtil.GetUeCppDefTypeHeaderFilePath(c.FullName); + var md5 = CacheFileUtil.GenMd5AndAddCache(file, content); + genCodeFiles.Add(new FileInfo() { FilePath = file, MD5 = md5 }); + })); + } + + int TYPE_PER_STUB_FILE = 200; + + for (int i = 0, n = (renderTypes.Count + TYPE_PER_STUB_FILE - 1) / TYPE_PER_STUB_FILE; i < n; i++) + { + int index = i; + tasks.Add(Task.Run(() => + { + int startIndex = index * TYPE_PER_STUB_FILE; + var content = FileHeaderUtil.ConcatAutoGenerationHeader( + render.RenderStub(renderTypes.GetRange(startIndex, Math.Min(TYPE_PER_STUB_FILE, renderTypes.Count - startIndex))), + ELanguage.CPP); + var file = $"stub_{index}.cpp"; + var md5 = CacheFileUtil.GenMd5AndAddCache(file, content); + genCodeFiles.Add(new FileInfo() { FilePath = file, MD5 = md5 }); + })); + } + break; + } + case "code_cs_unity_editor": + { + var render = new EditorCsRender(); + foreach (var c in ass.Types.Values) + { + tasks.Add(Task.Run(() => + { + var content = FileHeaderUtil.ConcatAutoGenerationHeader(render.RenderAny(c), ELanguage.CS); + var file = RenderFileUtil.GetDefTypePath(c.FullName, ELanguage.GO); + var md5 = CacheFileUtil.GenMd5AndAddCache(file, content); + genCodeFiles.Add(new FileInfo() { FilePath = file, MD5 = md5 }); + })); + } + break; + } + case "code_cpp_ue_bp": + { + var render = new UE4BpCppRender(); + foreach (var c in exportTypes) + { + if (!(c is DefEnum || c is DefBean)) + { + continue; + } + + tasks.Add(Task.Run(() => + { + var content = FileHeaderUtil.ConcatAutoGenerationHeader(render.RenderAny(c), ELanguage.CPP); + var file = "bp_" + RenderFileUtil.GetUeCppDefTypeHeaderFilePath(c.FullName); + var md5 = CacheFileUtil.GenMd5AndAddCache(file, content); + genCodeFiles.Add(new FileInfo() { FilePath = file, MD5 = md5 }); + })); + } + break; + } + case "data_bin": + case "data_json": + { + await CheckLoadCfgDataAsync(); + foreach (var c in exportTables) + { + tasks.Add(Task.Run(() => + { + var content = ToOutputData(c, ass.GetTableDataList(c), genType); + var file = genType.EndsWith("json") ? c.JsonOutputDataFile : c.OutputDataFile; + var md5 = FileUtil.CalcMD5(content); + CacheManager.Ins.AddCache(file, md5, content); + genDataFiles.Add(new FileInfo() { FilePath = file, MD5 = md5 }); + })); + } + break; + } + case "data_lua": + { + await CheckLoadCfgDataAsync(); + + tasks.Add(Task.Run(() => + { + var render = new LuaRender(); + var content = FileHeaderUtil.ConcatAutoGenerationHeader(render.RenderDefines(ass.Types.Values.ToList()), ELanguage.LUA); + var file = "Types.lua"; + var md5 = CacheFileUtil.GenMd5AndAddCache(file, content); + genDataFiles.Add(new FileInfo() { FilePath = file, MD5 = md5 }); + + })); + + foreach (var c in exportTables) + { + tasks.Add(Task.Run(() => + { + var content = ToOutputData(c, ass.GetTableDataList(c), genType); + var file = $"{c.Name}.lua"; + var md5 = FileUtil.CalcMD5(content); + CacheManager.Ins.AddCache(file, md5, content); + genDataFiles.Add(new FileInfo() { FilePath = file, MD5 = md5 }); + })); + } + break; + } + case "data_resources": + { + await CheckLoadCfgDataAsync(); + var genDataTasks = new List<Task<List<ResourceInfo>>>(); + foreach (var c in exportTables) + { + genDataTasks.Add(Task.Run(() => + { + return ExportResourceList(ass.GetTableDataList(c)); + })); + } + + tasks.Add(Task.Run(async () => + { + var ress = new HashSet<(string, string)>(10000); + var resourceLines = new List<string>(10000); + foreach (var task in genDataTasks) + { + foreach (var ri in await task) + { + if (ress.Add((ri.Resource, ri.Tag))) + { + resourceLines.Add($"{ri.Resource},{ri.Tag}"); + } + } + } + var file = "resources.txt"; + var contents = System.Text.Encoding.UTF8.GetBytes(string.Join("\n", resourceLines)); + var md5 = FileUtil.CalcMD5(contents); + CacheManager.Ins.AddCache(file, md5, contents); + + // 不这么处理??? + genDataFiles.Add(new FileInfo() { FilePath = file, MD5 = md5 }); + })); + break; + } + + default: + { + s_logger.Error("unknown gentype:{gentype}", genType); + break; + } + } + } + await Task.WhenAll(tasks.ToArray()); + + if (genCodeFiles.Count > 0) + { + res.FileGroups.Add(new FileGroup() { Dir = outputCodeDir, Files = genCodeFiles.ToList() }); + } + if (genDataFiles.Count > 0) + { + res.FileGroups.Add(new FileGroup() { Dir = outputDataDir, Files = genDataFiles.ToList() }); + } + } + catch (Exception e) + { + res.ErrCode = Luban.Common.EErrorCode.JOB_EXCEPTION; + res.ErrMsg = $"{e.Message} \n {e.StackTrace}"; + } + timer.EndPhaseAndLog(); + + agent.Session.ReplyRpc<GenJob, GenJobArg, GenJobRes>(rpc, res); + } + + public string GetActualFileName(string file) + { + int index = file.IndexOf('@'); + return index >= 0 ? file[(index + 1)..] : file; + } + + private List<DType> LoadCfgRecords(DefTable table, string originFile, string sheetName, byte[] content, bool multiRecord, bool exportTestData) + { + // (md5,sheet,multiRecord,exportTestData) -> (valuetype, List<(datas)>) + var dataSourc = DataSourceFactory.Create(originFile, sheetName, new MemoryStream(content), exportTestData); + try + { + List<DType> datas; + if (multiRecord) + { + datas = dataSourc.ReadMulti(table.ValueTType); + } + else + { + datas = new List<DType> { dataSourc.ReadOne(table.ValueTType) }; + } + foreach (var data in datas) + { + data.Source = originFile; + } + return datas; + } + catch (Exception e) + { + throw new Exception($"配置文件:{originFile} 生成失败. ==> {e.Message}", e); + } + } + + class InputFileInfo + { + public string MD5 { get; set; } + + public string OriginFile { get; set; } + + public string ActualFile { get; set; } + + public string SheetName { get; set; } + } + + + + private async Task<List<InputFileInfo>> CollectInputFilesAsync(RemoteAgent agent, DefTable table, string dataDir) + { + var collectTasks = new List<Task<List<InputFileInfo>>>(); + foreach (var file in table.InputFiles) + { + (var actualFile, var sheetName) = RenderFileUtil.SplitFileAndSheetName(FileUtil.Standardize(file)); + var actualFullPath = FileUtil.Combine(dataDir, actualFile); + var originFullPath = FileUtil.Combine(dataDir, file); + //s_logger.Info("== get input file:{file} actualFile:{actual}", file, actualFile); + + collectTasks.Add(Task.Run(async () => + { + var fileOrDirContent = await agent.GetFileOrDirectoryAsync(actualFullPath); + if (fileOrDirContent.IsFile) + { + return new List<InputFileInfo> { new InputFileInfo() { OriginFile = file, ActualFile = actualFullPath, SheetName = sheetName, MD5 = fileOrDirContent.Md5 } }; + } + else + { + return fileOrDirContent.SubFiles.Select(f => new InputFileInfo() { OriginFile = f.FilePath, ActualFile = f.FilePath, MD5 = f.MD5 }).ToList(); + } + })); + } + + var allFiles = new List<InputFileInfo>(); + foreach (var t in collectTasks) + { + allFiles.AddRange(await t); + } + return allFiles; + } + + public async Task LoadTableAsync(RemoteAgent agent, DefTable table, string dataDir, bool exportTestData) + { + var tasks = new List<Task<List<DType>>>(); + + var inputFiles = await CollectInputFilesAsync(agent, table, dataDir); + + // check cache (table, exporttestdata) -> (list<InputFileInfo>, List<DType>) + // (md5, sheetName,exportTestData) -> (value_type, List<DType>) + + foreach (var file in inputFiles) + { + var actualFile = file.ActualFile; + //s_logger.Info("== get input file:{file} actualFile:{actual}", file, actualFile); + + tasks.Add(Task.Run(async () => + { + if (FileRecordCacheManager.Ins.TryGetCacheLoadedRecords(table, file.MD5, actualFile, file.SheetName, exportTestData, out var cacheRecords)) + { + return cacheRecords; + } + var res = LoadCfgRecords(table, + file.OriginFile, + file.SheetName, + await agent.GetFromCacheOrReadAllBytesAsync(file.ActualFile, file.MD5), + RenderFileUtil.IsExcelFile(file.ActualFile), + exportTestData); + + FileRecordCacheManager.Ins.AddCacheLoadedRecords(table, file.MD5, file.SheetName, exportTestData, res); + + return res; + })); + } + + var records = new List<DType>(tasks.Count); + foreach (var task in tasks) + { + records.AddRange(await task); + } + + s_logger.Trace("== load recors. count:{count}", records.Count); + + table.Assembly.AddDataTable(table, records); + + s_logger.Trace("table:{name} record num:{num}", table.FullName, records.Count); + } + + private byte[] ToOutputData(DefTable table, List<DType> records, string dataType) + { + switch (dataType) + { + case "data_bin": + { + var buf = ThreadLocalTemporalByteBufPool.Alloc(1024 * 1024); + BinaryExportor.Ins.WriteList(records, buf); + var bytes = buf.CopyData(); + ThreadLocalTemporalByteBufPool.Free(buf); + return bytes; + } + case "data_json": + { + var ss = new MemoryStream(); + var jsonWriter = new Utf8JsonWriter(ss, new JsonWriterOptions() + { + Indented = true, + SkipValidation = false, + }); + JsonExportor.Ins.WriteList(records, jsonWriter); + jsonWriter.Flush(); + return DataUtil.StreamToBytes(ss); + } + case "data_lua": + { + var content = new List<string>(); + + switch (table.Mode) + { + case ETableMode.ONE: + { + LuaExportor.Ins.ExportTableOne(table, records, content); + break; + } + case ETableMode.MAP: + { + LuaExportor.Ins.ExportTableOneKeyMap(table, records, content); + break; + } + case ETableMode.BMAP: + { + LuaExportor.Ins.ExportTableTwoKeyMap(table, records, content); + break; + } + default: + { + throw new NotSupportedException(); + } + } + return System.Text.Encoding.UTF8.GetBytes(string.Join('\n', content)); + } + default: + { + throw new ArgumentException($"not support datatype:{dataType}"); + } + } + } + + private List<ResourceInfo> ExportResourceList(List<DType> records) + { + var resList = new List<ResourceInfo>(); + foreach (DBean res in records) + { + ResourceExportor.Ins.Accept(res, null, resList); + } + return resList; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/RawDefs/CfgBean.cs b/src/Luban.Job.Cfg/Source/RawDefs/CfgBean.cs new file mode 100644 index 0000000..9790d7f --- /dev/null +++ b/src/Luban.Job.Cfg/Source/RawDefs/CfgBean.cs @@ -0,0 +1,11 @@ +using Luban.Job.Common.RawDefs; + +namespace Luban.Config.Common.RawDefs +{ + public class CfgBean : Bean + { + public string Alias { get; set; } + + public string Sep { get; set; } + } +} diff --git a/src/Luban.Job.Cfg/Source/RawDefs/CfgField.cs b/src/Luban.Job.Cfg/Source/RawDefs/CfgField.cs new file mode 100644 index 0000000..a6014e3 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/RawDefs/CfgField.cs @@ -0,0 +1,34 @@ +using Luban.Job.Common.RawDefs; +using System.Collections.Generic; + +namespace Luban.Config.Common.RawDefs +{ + + public class Validator + { + public string Type { get; set; } + + public string Rule { get; set; } + } + + public class CfgField : Field + { + public string Index { get; set; } = ""; + + public string Sep { get; set; } = ""; + + public bool IsMultiRow { get; set; } + + public string Resource { get; set; } = ""; + + public string Converter { get; set; } = ""; + + public List<string> Groups { get; set; } = new List<string>(); + + public List<Validator> KeyValidators { get; } = new List<Validator>(); + + public List<Validator> ValueValidators { get; } = new List<Validator>(); + + public List<Validator> Validators { get; } = new List<Validator>(); + } +} diff --git a/src/Luban.Job.Cfg/Source/RawDefs/Defines.cs b/src/Luban.Job.Cfg/Source/RawDefs/Defines.cs new file mode 100644 index 0000000..4b4ed22 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/RawDefs/Defines.cs @@ -0,0 +1,22 @@ +using Luban.Job.Common.RawDefs; +using System.Collections.Generic; + +namespace Luban.Config.Common.RawDefs +{ + public class Defines + { + public string TopModule { get; set; } = ""; + + public List<Bean> Beans { get; set; } = new List<Bean>(); + + public List<Const> Consts { get; set; } = new List<Const>(); + + public List<PEnum> Enums { get; set; } = new List<PEnum>(); + + public List<Table> Tables { get; set; } = new List<Table>(); + + public List<Group> Groups { get; set; } = new List<Group>(); + + public List<Service> Services { get; set; } = new List<Service>(); + } +} diff --git a/src/Luban.Job.Cfg/Source/RawDefs/Group.cs b/src/Luban.Job.Cfg/Source/RawDefs/Group.cs new file mode 100644 index 0000000..bd63340 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/RawDefs/Group.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Luban.Config.Common.RawDefs +{ + public class Group + { + public List<string> Names { get; set; } = new List<string>(); + } +} diff --git a/src/Luban.Job.Cfg/Source/RawDefs/ResourceInfo.cs b/src/Luban.Job.Cfg/Source/RawDefs/ResourceInfo.cs new file mode 100644 index 0000000..544ed59 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/RawDefs/ResourceInfo.cs @@ -0,0 +1,9 @@ +namespace Luban.Job.Cfg.RawDefs +{ + public class ResourceInfo + { + public string Resource { get; set; } + + public string Tag { get; set; } + } +} diff --git a/src/Luban.Job.Cfg/Source/RawDefs/Service.cs b/src/Luban.Job.Cfg/Source/RawDefs/Service.cs new file mode 100644 index 0000000..59bd7c9 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/RawDefs/Service.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Luban.Config.Common.RawDefs +{ + public class Service + { + public string Name { get; set; } + + public string Manager { get; set; } + + public List<string> Groups { get; set; } = new List<string>(); + + public List<string> Refs { get; set; } = new List<string>(); + } +} diff --git a/src/Luban.Job.Cfg/Source/RawDefs/Table.cs b/src/Luban.Job.Cfg/Source/RawDefs/Table.cs new file mode 100644 index 0000000..038547e --- /dev/null +++ b/src/Luban.Job.Cfg/Source/RawDefs/Table.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; + +namespace Luban.Config.Common.RawDefs +{ + public enum ETableMode + { + ONE, + MAP, + BMAP, //双主键 map + } + + public class CfgInputFile + { + public string OriginFile { get; set; } + + public string ActualFile { get; set; } + + public string MD5 { get; set; } + + public bool MultiRecord { get; set; } + } + + public class Table + { + public string Namespace { get; set; } + + public string Name { get; set; } + + public string Index { get; set; } + + public string ValueType { get; set; } + + public ETableMode Mode { get; set; } + + public List<string> Groups { get; set; } = new List<string>(); + + public List<string> InputFiles { get; set; } = new List<string>(); + } +} diff --git a/src/Luban.Job.Cfg/Source/RenderExtension.cs b/src/Luban.Job.Cfg/Source/RenderExtension.cs new file mode 100644 index 0000000..fb5af5e --- /dev/null +++ b/src/Luban.Job.Cfg/Source/RenderExtension.cs @@ -0,0 +1,27 @@ +using Luban.Job.Cfg.Defs; +using Scriban; +using System.Collections.Generic; + +namespace Luban.Job.Cfg +{ + public static class RenderExtension + { + public static string RenderCode(this Template template, object model, Dictionary<string, object> extraModels = null) + { + var ctx = new TemplateContext(); + var env = new TTypeTemplateExtends + { + ["x"] = model + }; + if (extraModels != null) + { + foreach ((var k, var v) in extraModels) + { + env[k] = v; + } + } + ctx.PushGlobal(env); + return template.Render(ctx); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TTypeExtensions.cs b/src/Luban.Job.Cfg/Source/TTypeExtensions.cs new file mode 100644 index 0000000..7a3c786 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TTypeExtensions.cs @@ -0,0 +1,13 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Cfg +{ + static class TTypeExtensions + { + public static string CsUnderingDefineType(this TType type) + { + return type.Apply(CsUnderingDefineTypeName.Ins); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/CollectEditorCppForwardDefineVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/CollectEditorCppForwardDefineVisitor.cs new file mode 100644 index 0000000..c2619fd --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/CollectEditorCppForwardDefineVisitor.cs @@ -0,0 +1,129 @@ +using Luban.Job.Common.Defs; +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System; +using System.Collections.Generic; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class CollectEditorCppForwardDefineVisitor : ITypeActionVisitor<HashSet<DefTypeBase>> + { + public static CollectEditorCppForwardDefineVisitor Ins { get; } = new CollectEditorCppForwardDefineVisitor(); + + public void Accept(TBool type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TByte type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TShort type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TFshort type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TInt type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TFint type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TLong type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TFlong type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TFloat type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TDouble type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TEnum type, HashSet<DefTypeBase> x) + { + //x.Add(type.DefineEnum); + } + + public void Accept(TString type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TBytes type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TText type, HashSet<DefTypeBase> x) + { + throw new NotImplementedException(); + } + + public void Accept(TBean type, HashSet<DefTypeBase> x) + { + x.Add(type.Bean); + } + + public void Accept(TArray type, HashSet<DefTypeBase> x) + { + type.ElementType.Apply(this, x); + } + + public void Accept(TList type, HashSet<DefTypeBase> x) + { + type.ElementType.Apply(this, x); + } + + public void Accept(TSet type, HashSet<DefTypeBase> x) + { + type.ElementType.Apply(this, x); + } + + public void Accept(TMap type, HashSet<DefTypeBase> x) + { + type.KeyType.Apply(this, x); + type.ValueType.Apply(this, x); + } + + public void Accept(TDateTime type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TVector2 type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TVector3 type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TVector4 type, HashSet<DefTypeBase> x) + { + + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/CollectEditorCppHeaderIncludeVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/CollectEditorCppHeaderIncludeVisitor.cs new file mode 100644 index 0000000..5ec669d --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/CollectEditorCppHeaderIncludeVisitor.cs @@ -0,0 +1,129 @@ +using Luban.Job.Common.Defs; +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System; +using System.Collections.Generic; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class CollectEditorCppHeaderIncludeVisitor : ITypeActionVisitor<HashSet<DefTypeBase>> + { + public static CollectEditorCppHeaderIncludeVisitor Ins { get; } = new CollectEditorCppHeaderIncludeVisitor(); + + public void Accept(TBool type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TByte type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TShort type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TFshort type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TInt type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TFint type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TLong type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TFlong type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TFloat type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TDouble type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TEnum type, HashSet<DefTypeBase> x) + { + x.Add(type.DefineEnum); + } + + public void Accept(TString type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TBytes type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TText type, HashSet<DefTypeBase> x) + { + throw new NotImplementedException(); + } + + public void Accept(TBean type, HashSet<DefTypeBase> x) + { + x.Add(type.Bean); + } + + public void Accept(TArray type, HashSet<DefTypeBase> x) + { + type.ElementType.Apply(this, x); + } + + public void Accept(TList type, HashSet<DefTypeBase> x) + { + type.ElementType.Apply(this, x); + } + + public void Accept(TSet type, HashSet<DefTypeBase> x) + { + type.ElementType.Apply(this, x); + } + + public void Accept(TMap type, HashSet<DefTypeBase> x) + { + type.KeyType.Apply(this, x); + type.ValueType.Apply(this, x); + } + + public void Accept(TVector2 type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TVector3 type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TVector4 type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TDateTime type, HashSet<DefTypeBase> x) + { + + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/CollectEditorCppIncludeVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/CollectEditorCppIncludeVisitor.cs new file mode 100644 index 0000000..b484e2a --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/CollectEditorCppIncludeVisitor.cs @@ -0,0 +1,150 @@ +using Luban.Job.Cfg.Defs; +using Luban.Job.Common.Defs; +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System; +using System.Collections.Generic; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class CollectEditorCppIncludeVisitor : ITypeActionVisitor<HashSet<DefTypeBase>> + { + public static CollectEditorCppIncludeVisitor Ins { get; } = new CollectEditorCppIncludeVisitor(); + + + public void Collect(DefBean bean, HashSet<DefTypeBase> x) + { + if (x.Add(bean)) + { + foreach (var f in bean.Fields) + { + f.CType.Apply(this, x); + } + + if (bean.IsAbstractType) + { + foreach (DefBean c in bean.HierarchyNotAbstractChildren) + { + Collect(c, x); + } + } + } + } + + public void Accept(TBool type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TByte type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TShort type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TFshort type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TInt type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TFint type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TLong type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TFlong type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TFloat type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TDouble type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TEnum type, HashSet<DefTypeBase> x) + { + x.Add(type.DefineEnum); + } + + public void Accept(TString type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TBytes type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TText type, HashSet<DefTypeBase> x) + { + throw new NotImplementedException(); + } + + public void Accept(TBean type, HashSet<DefTypeBase> x) + { + Collect((DefBean)type.Bean, x); + } + + public void Accept(TArray type, HashSet<DefTypeBase> x) + { + type.ElementType.Apply(this, x); + } + + public void Accept(TList type, HashSet<DefTypeBase> x) + { + type.ElementType.Apply(this, x); + } + + public void Accept(TSet type, HashSet<DefTypeBase> x) + { + type.ElementType.Apply(this, x); + } + + public void Accept(TMap type, HashSet<DefTypeBase> x) + { + type.KeyType.Apply(this, x); + type.ValueType.Apply(this, x); + } + + public void Accept(TVector2 type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TVector3 type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TVector4 type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TDateTime type, HashSet<DefTypeBase> x) + { + + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/CollectGoImport.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/CollectGoImport.cs new file mode 100644 index 0000000..89bdb89 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/CollectGoImport.cs @@ -0,0 +1,129 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System.Collections.Generic; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class CollectGoImport : ITypeActionVisitor<HashSet<string>> + { + public static CollectGoImport Ins { get; } = new CollectGoImport(); + + public void Accept(TBool type, HashSet<string> x) + { + + } + + public void Accept(TByte type, HashSet<string> x) + { + + } + + public void Accept(TShort type, HashSet<string> x) + { + + } + + public void Accept(TFshort type, HashSet<string> x) + { + + } + + public void Accept(TInt type, HashSet<string> x) + { + + } + + public void Accept(TFint type, HashSet<string> x) + { + + } + + public void Accept(TLong type, HashSet<string> x) + { + + } + + public void Accept(TFlong type, HashSet<string> x) + { + + } + + public void Accept(TFloat type, HashSet<string> x) + { + + } + + public void Accept(TDouble type, HashSet<string> x) + { + + } + + public void Accept(TEnum type, HashSet<string> x) + { + + } + + public void Accept(TString type, HashSet<string> x) + { + + } + + public void Accept(TBytes type, HashSet<string> x) + { + + } + + public void Accept(TText type, HashSet<string> x) + { + + } + + public void Accept(TBean type, HashSet<string> x) + { + + } + + public void Accept(TArray type, HashSet<string> x) + { + type.ElementType.Apply(this, x); + } + + public void Accept(TList type, HashSet<string> x) + { + + type.ElementType.Apply(this, x); + } + + public void Accept(TSet type, HashSet<string> x) + { + type.ElementType.Apply(this, x); + } + + public void Accept(TMap type, HashSet<string> x) + { + + type.KeyType.Apply(this, x); + type.ValueType.Apply(this, x); + } + + public void Accept(TVector2 type, HashSet<string> x) + { + x.Add("bright/math"); + } + + public void Accept(TVector3 type, HashSet<string> x) + { + x.Add("bright/math"); + } + + public void Accept(TVector4 type, HashSet<string> x) + { + x.Add("bright/math"); + } + + public void Accept(TDateTime type, HashSet<string> x) + { + + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/CollectUeCppForwardDefineVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/CollectUeCppForwardDefineVisitor.cs new file mode 100644 index 0000000..f4ab152 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/CollectUeCppForwardDefineVisitor.cs @@ -0,0 +1,130 @@ +using Luban.Job.Cfg.Defs; +using Luban.Job.Common.Defs; +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System; +using System.Collections.Generic; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class CollectUeCppForwardDefineVisitor : ITypeActionVisitor<HashSet<DefTypeBase>> + { + public static CollectUeCppForwardDefineVisitor Ins { get; } = new CollectUeCppForwardDefineVisitor(); + + public void Accept(TBool type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TByte type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TShort type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TFshort type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TInt type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TFint type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TLong type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TFlong type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TFloat type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TDouble type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TEnum type, HashSet<DefTypeBase> x) + { + //x.Add(type.DefineEnum); + } + + public void Accept(TString type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TBytes type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TText type, HashSet<DefTypeBase> x) + { + throw new NotImplementedException(); + } + + public void Accept(TBean type, HashSet<DefTypeBase> x) + { + x.Add(type.GetBeanAs<DefBean>()); + } + + public void Accept(TArray type, HashSet<DefTypeBase> x) + { + type.ElementType.Apply(this, x); + } + + public void Accept(TList type, HashSet<DefTypeBase> x) + { + type.ElementType.Apply(this, x); + } + + public void Accept(TSet type, HashSet<DefTypeBase> x) + { + type.ElementType.Apply(this, x); + } + + public void Accept(TMap type, HashSet<DefTypeBase> x) + { + type.KeyType.Apply(this, x); + type.ValueType.Apply(this, x); + } + + public void Accept(TVector2 type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TVector3 type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TVector4 type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TDateTime type, HashSet<DefTypeBase> x) + { + + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/CollectUeCppIncludeVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/CollectUeCppIncludeVisitor.cs new file mode 100644 index 0000000..9ff352c --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/CollectUeCppIncludeVisitor.cs @@ -0,0 +1,129 @@ +using Luban.Job.Common.Defs; +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System; +using System.Collections.Generic; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class CollectUeCppIncludeVisitor : ITypeActionVisitor<HashSet<DefTypeBase>> + { + public static CollectUeCppIncludeVisitor Ins { get; } = new CollectUeCppIncludeVisitor(); + + public void Accept(TBool type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TByte type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TShort type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TFshort type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TInt type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TFint type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TLong type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TFlong type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TFloat type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TDouble type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TEnum type, HashSet<DefTypeBase> x) + { + x.Add(type.DefineEnum); + } + + public void Accept(TString type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TBytes type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TText type, HashSet<DefTypeBase> x) + { + throw new NotImplementedException(); + } + + public void Accept(TBean type, HashSet<DefTypeBase> x) + { + //x.Add(type.Bean); + } + + public void Accept(TArray type, HashSet<DefTypeBase> x) + { + type.ElementType.Apply(this, x); + } + + public void Accept(TList type, HashSet<DefTypeBase> x) + { + type.ElementType.Apply(this, x); + } + + public void Accept(TSet type, HashSet<DefTypeBase> x) + { + type.ElementType.Apply(this, x); + } + + public void Accept(TMap type, HashSet<DefTypeBase> x) + { + type.KeyType.Apply(this, x); + type.ValueType.Apply(this, x); + } + + public void Accept(TVector2 type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TVector3 type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TVector4 type, HashSet<DefTypeBase> x) + { + + } + + public void Accept(TDateTime type, HashSet<DefTypeBase> x) + { + + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/CppDeserializeVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/CppDeserializeVisitor.cs new file mode 100644 index 0000000..866f68d --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/CppDeserializeVisitor.cs @@ -0,0 +1,27 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class CppDeserializeVisitor : DecoratorFuncVisitor<string, string, string> + { + public static CppDeserializeVisitor Ins { get; } = new CppDeserializeVisitor(); + + public override string DoAccept(TType type, string bufName, string fieldName) + { + if (type.IsNullable) + { + return $"{{ bool _read_succ_; if(!{bufName}.readBool(_read_succ_)){{return false;}} if(_read_succ_) {{ {type.Apply(CppUnderingDeserializeVisitor.Ins, bufName, fieldName)} }} else {{ {fieldName} = {{}}; }} }}"; + } + else + { + return type.Apply(CppUnderingDeserializeVisitor.Ins, bufName, fieldName); + } + } + + public override string Accept(TBean type, string bufName, string fieldName) + { + return type.Apply(CppUnderingDeserializeVisitor.Ins, bufName, fieldName); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/CppUnderingDeserializeVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/CppUnderingDeserializeVisitor.cs new file mode 100644 index 0000000..8d7dd93 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/CppUnderingDeserializeVisitor.cs @@ -0,0 +1,126 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class CppUnderingDeserializeVisitor : ITypeFuncVisitor<string, string, string> + { + public static CppUnderingDeserializeVisitor Ins { get; } = new CppUnderingDeserializeVisitor(); + + public string Accept(TBool type, string bufName, string fieldName) + { + return $"if (!{bufName}.readBool({fieldName})) return false;"; + } + + public string Accept(TByte type, string bufName, string fieldName) + { + return $"if(!{bufName}.readByte({fieldName})) return false;"; + } + + public string Accept(TShort type, string bufName, string fieldName) + { + return $"if(!{bufName}.readShort({fieldName})) return false;"; + } + + public string Accept(TFshort type, string bufName, string fieldName) + { + return $"if(!{bufName}.readFshort({fieldName})) return false;"; + } + + public string Accept(TInt type, string bufName, string fieldName) + { + return $"if(!{bufName}.readInt({fieldName})) return false;"; + } + + public string Accept(TFint type, string bufName, string fieldName) + { + return $"if(!{bufName}.readFint({fieldName})) return false;"; + } + + public string Accept(TLong type, string bufName, string fieldName) + { + return $"if(!{bufName}.readLong({fieldName})) return false;"; + } + + public string Accept(TFlong type, string bufName, string fieldName) + { + return $"if(!{bufName}.readFlong({fieldName})) return false;"; + } + + public string Accept(TFloat type, string bufName, string fieldName) + { + return $"if(!{bufName}.readFloat({fieldName})) return false;"; + } + + public string Accept(TDouble type, string bufName, string fieldName) + { + return $"if(!{bufName}.readDouble({fieldName})) return false;"; + } + + public string Accept(TEnum type, string bufName, string fieldName) + { + return $"{{int _temp_; if(!{bufName}.readInt(_temp_)) return false; {fieldName} = {type.DefineEnum.CppFullName}(_temp_); }}"; + } + + public string Accept(TString type, string bufName, string fieldName) + { + return $"if(!BYTEBUF_READ_STRING({bufName}, {fieldName})) return false;"; + } + + public string Accept(TBytes type, string bufName, string fieldName) + { + return $"if(!{bufName}.readBytes({fieldName})) return false;"; + } + + public string Accept(TText type, string bufName, string fieldName) + { + return $"if(!{bufName}.readString({fieldName})) return false;"; + } + + public string Accept(TBean type, string bufName, string fieldName) + { + return $"if(!{type.Bean.CppFullName}::deserialize{type.Bean.Name}({bufName}, {fieldName})) return false;"; + } + + public string Accept(TArray type, string bufName, string fieldName) + { + return $"{{int32_t n; if(!{bufName}.readSize(n)) return false; n = std::min(n, {bufName}.size());{fieldName}.reserve(n);for(int i = 0 ; i < n ; i++) {{ {type.ElementType.Apply(CppDefineTypeName.Ins)} _e;{type.ElementType.Apply(this, bufName, "_e")} {fieldName}.push_back(_e);}}}}"; + } + + public string Accept(TList type, string bufName, string fieldName) + { + return $"{{int32_t n; if(!{bufName}.readSize(n)) return false; n = std::min(n, {bufName}.size()); {fieldName}.reserve(n);for(int i = 0 ; i < n ; i++) {{ {type.ElementType.Apply(CppDefineTypeName.Ins)} _e; {type.ElementType.Apply(this, bufName, "_e")} {fieldName}.push_back(_e);}}}}"; + } + + public string Accept(TSet type, string bufName, string fieldName) + { + return $"{{int32_t n; if(!{bufName}.readSize(n)) return false; n = std::min(n, {bufName}.size()); {fieldName}.reserve(n * 3 / 2);for(int i = 0 ; i < n ; i++) {{ {type.ElementType.Apply(CppDefineTypeName.Ins)} _e; {type.ElementType.Apply(this, bufName, "_e")} {fieldName}.insert(_e);}}}}"; + } + + public string Accept(TMap type, string bufName, string fieldName) + { + return $"{{int32_t n; if(!{bufName}.readSize(n)) return false; n = std::min(n, {bufName}.size()); {fieldName}.reserve(n * 3 / 2);for(int i = 0 ; i < n ; i++) {{ {type.KeyType.Apply(CppDefineTypeName.Ins)} _k; {type.KeyType.Apply(this, bufName, "_k")} {type.ValueType.Apply(CppDefineTypeName.Ins)} _v; {type.ValueType.Apply(this, bufName, "_v")} {fieldName}[_k] = _v;}}}}"; + + } + + public string Accept(TVector2 type, string bufName, string fieldName) + { + return $"if(!{bufName}.readVector2({fieldName})) return false;"; + } + + public string Accept(TVector3 type, string bufName, string fieldName) + { + return $"if(!{bufName}.readVector3({fieldName})) return false;"; + } + + public string Accept(TVector4 type, string bufName, string fieldName) + { + return $"if(!{bufName}.readVector4({fieldName})) return false;"; + } + + public string Accept(TDateTime type, string bufName, string fieldName) + { + return $"if(!{bufName}.readInt({fieldName})) return false;"; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/CsDeserializeVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/CsDeserializeVisitor.cs new file mode 100644 index 0000000..4df90ff --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/CsDeserializeVisitor.cs @@ -0,0 +1,27 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class CsDeserializeVisitor : DecoratorFuncVisitor<string, string, string> + { + public static CsDeserializeVisitor Ins { get; } = new CsDeserializeVisitor(); + + public override string DoAccept(TType type, string bufName, string fieldName) + { + if (type.IsNullable) + { + return $"if({bufName}.ReadBool()){{ {type.Apply(CsUnderingDeserializeVisitor.Ins, bufName, fieldName)} }} else {{ {fieldName} = null; }}"; + } + else + { + return type.Apply(CsUnderingDeserializeVisitor.Ins, bufName, fieldName); + } + } + + public override string Accept(TBean type, string bufName, string fieldName) + { + return type.Apply(CsUnderingDeserializeVisitor.Ins, bufName, fieldName); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/CsJsonUmarshal.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/CsJsonUmarshal.cs new file mode 100644 index 0000000..bdc2912 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/CsJsonUmarshal.cs @@ -0,0 +1,126 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class CsJsonUserialize : ITypeFuncVisitor<string, string, string> + { + public static CsJsonUserialize Ins { get; } = new CsJsonUserialize(); + + public string Accept(TBool type, string json, string x) + { + return $"{x} = {json}.GetBoolean();"; + } + + public string Accept(TByte type, string json, string x) + { + return $"{x} = {json}.GetByte();"; + } + + public string Accept(TShort type, string json, string x) + { + return $"{x} = {json}.GetInt16();"; + } + + public string Accept(TFshort type, string json, string x) + { + return $"{x} = {json}.GetInt16();"; + } + + public string Accept(TInt type, string json, string x) + { + return $"{x} = {json}.GetInt32();"; + } + + public string Accept(TFint type, string json, string x) + { + return $"{x} = {json}.GetInt32();"; + } + + public string Accept(TLong type, string json, string x) + { + return $"{x} = {json}.GetInt64();"; + } + + public string Accept(TFlong type, string json, string x) + { + return $"{x} = {json}.GetInt64();"; + } + + public string Accept(TFloat type, string json, string x) + { + return $"{x} = {json}.GetSingle();"; + } + + public string Accept(TDouble type, string json, string x) + { + return $"{x} = {json}.GetDouble();"; + } + + public string Accept(TEnum type, string json, string x) + { + return $"{x} = ({type.CsUnderingDefineType()}){json}.GetInt32();"; + } + + public string Accept(TString type, string json, string x) + { + return $"{x} = {json}.GetString();"; + } + + public string Accept(TBytes type, string json, string x) + { + throw new NotSupportedException(); + } + + public string Accept(TText type, string json, string x) + { + return $"{x} = {json}.GetString();"; + } + + public string Accept(TBean type, string json, string x) + { + return $"{x} = {type.CsUnderingDefineType()}.Deserialize{type.Bean.Name}({json});"; + } + + public string Accept(TArray type, string json, string x) + { + return $"{{ var _json = {json}; int _n = _json.GetArrayLength(); {x} = new {type.ElementType.CsUnderingDefineType()}[_n]; int _index=0; foreach(JsonElement __e in _json.EnumerateArray()) {{ {type.ElementType.CsUnderingDefineType()} __v; {type.ElementType.Apply(this, "__e", "__v")} {x}[_index++] = __v; }} }}"; + } + + public string Accept(TList type, string json, string x) + { + return $"{{ var _json = {json}; {x} = new {type.CsUnderingDefineType()}(_json.GetArrayLength()); foreach(JsonElement __e in _json.EnumerateArray()) {{ {type.ElementType.CsUnderingDefineType()} __v; {type.ElementType.Apply(this, "__e", "__v")} {x}.Add(__v); }} }}"; + } + + public string Accept(TSet type, string json, string x) + { + return $"{{ var _json = {json}; {x} = new {type.CsUnderingDefineType()}(_json.GetArrayLength()); foreach(JsonElement __e in _json.EnumerateArray()) {{ {type.ElementType.CsUnderingDefineType()} __v; {type.ElementType.Apply(this, "__e", "__v")} {x}.Add(__v); }} }}"; + } + + public string Accept(TMap type, string json, string x) + { + return @$"{{ var _json = {json}; {x} = new {type.CsUnderingDefineType()}(_json.GetArrayLength()); foreach(JsonElement __e in _json.EnumerateArray()) {{ {type.KeyType.CsUnderingDefineType()} __k; {type.KeyType.Apply(this, "__e[0]", "__k")} {type.ValueType.CsUnderingDefineType()} __v; {type.ValueType.Apply(this, "__e[1]", "__v")} {x}.Add(__k, __v); }} }}"; + } + + public string Accept(TVector2 type, string json, string x) + { + return $"{{ var _json = {json}; float __x; {TFloat.Ins.Apply(this, "_json.GetProperty(\"x\")", "__x") } float __y; {TFloat.Ins.Apply(this, "_json.GetProperty(\"y\")", "__y") } {x} = new System.Numerics.Vector2(__x, __y); }}"; + } + + public string Accept(TVector3 type, string json, string x) + { + return $"{{ var _json = {json}; float __x; {TFloat.Ins.Apply(this, "_json.GetProperty(\"x\")", "__x") } float __y; {TFloat.Ins.Apply(this, "_json.GetProperty(\"y\")", "__y") } float __z; {TFloat.Ins.Apply(this, "_json.GetProperty(\"z\")", "__z") } {x} = new System.Numerics.Vector3(__x, __y,__z); }}"; + } + + public string Accept(TVector4 type, string json, string x) + { + return $"{{ var _json = {json}; float __x; {TFloat.Ins.Apply(this, "_json.GetProperty(\"x\")", "__x") } float __y; {TFloat.Ins.Apply(this, "_json.GetProperty(\"y\")", "__y") } float __z; {TFloat.Ins.Apply(this, "_json.GetProperty(\"z\")", "__z") } float __w; {TFloat.Ins.Apply(this, "_json.GetProperty(\"w\")", "__w") } {x} = new System.Numerics.Vector4(__x, __y, __z, __w); }}"; + } + + public string Accept(TDateTime type, string json, string x) + { + return $"{x} = {json}.GetInt32();"; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/CsRecursiveResolveVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/CsRecursiveResolveVisitor.cs new file mode 100644 index 0000000..ca1bb2a --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/CsRecursiveResolveVisitor.cs @@ -0,0 +1,126 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class CsRecursiveResolveVisitor : ITypeFuncVisitor<string, string, string> + { + public static CsRecursiveResolveVisitor Ins { get; } = new CsRecursiveResolveVisitor(); + + public string Accept(TBool type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TByte type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TShort type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TFshort type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TInt type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TFint type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TLong type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TFlong type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TFloat type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TDouble type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TEnum type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TString type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TBytes type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TText type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TBean type, string fieldName, string tablesName) + { + return $"{fieldName}?.Resolve({tablesName});"; + } + + public string Accept(TArray type, string fieldName, string tablesName) + { + return $@"foreach(var _e in {fieldName}) {{ _e?.Resolve({tablesName}); }}"; + } + + public string Accept(TList type, string fieldName, string tablesName) + { + return $@"foreach(var _e in {fieldName}) {{ _e?.Resolve({tablesName}); }}"; + } + + public string Accept(TSet type, string fieldName, string tablesName) + { + return $@"foreach(var _e in {fieldName}) {{ _e?.Resolve({tablesName}); }}"; + } + + public string Accept(TMap type, string fieldName, string tablesName) + { + return $@"foreach(var _e in {fieldName}.Values) {{ _e?.Resolve({tablesName}); }}"; + } + + public string Accept(TVector2 type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TVector3 type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TVector4 type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TDateTime type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/CsUnderingDeserializeVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/CsUnderingDeserializeVisitor.cs new file mode 100644 index 0000000..892afc4 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/CsUnderingDeserializeVisitor.cs @@ -0,0 +1,126 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class CsUnderingDeserializeVisitor : ITypeFuncVisitor<string, string, string> + { + public static CsUnderingDeserializeVisitor Ins { get; } = new CsUnderingDeserializeVisitor(); + + public string Accept(TBool type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.ReadBool();"; + } + + public string Accept(TByte type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.ReadByte();"; + } + + public string Accept(TShort type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.ReadShort();"; + } + + public string Accept(TFshort type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.ReadFshort();"; + } + + public string Accept(TInt type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.ReadInt();"; + } + + public string Accept(TFint type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.ReadFint();"; + } + + public string Accept(TLong type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.ReadLong();"; + } + + public string Accept(TFlong type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.ReadFlong();"; + } + + public string Accept(TFloat type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.ReadFloat();"; + } + + public string Accept(TDouble type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.ReadDouble();"; + } + + public string Accept(TEnum type, string bufName, string fieldName) + { + return $"{fieldName} = ({type.DefineEnum.FullName}){bufName}.ReadInt();"; + } + + public string Accept(TString type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.ReadString();"; + } + + public string Accept(TBytes type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.ReadBytes();"; + } + + public string Accept(TText type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.ReadString();"; + } + + public string Accept(TBean type, string bufName, string fieldName) + { + return $"{fieldName} = {type.Bean.FullName}.Deserialize{type.Bean.Name}({bufName});"; + } + + public string Accept(TArray type, string bufName, string fieldName) + { + return $"{{int n = System.Math.Min({bufName}.ReadSize(), {bufName}.Size);{fieldName} = new {type.ElementType.Apply(CsDefineTypeName.Ins)}[n];for(var i = 0 ; i < n ; i++) {{ {type.ElementType.Apply(CsDefineTypeName.Ins)} _e;{type.ElementType.Apply(this, bufName, "_e")} {fieldName}[i] = _e;}}}}"; + } + + public string Accept(TList type, string bufName, string fieldName) + { + return $"{{int n = System.Math.Min({bufName}.ReadSize(), {bufName}.Size);{fieldName} = new {type.Apply(CsDefineTypeName.Ins)}(n);for(var i = 0 ; i < n ; i++) {{ {type.ElementType.Apply(CsDefineTypeName.Ins)} _e; {type.ElementType.Apply(this, bufName, "_e")} {fieldName}.Add(_e);}}}}"; + } + + public string Accept(TSet type, string bufName, string fieldName) + { + return $"{{int n = System.Math.Min({bufName}.ReadSize(), {bufName}.Size);{fieldName} = new {type.Apply(CsDefineTypeName.Ins)}(/*n * 3 / 2*/);for(var i = 0 ; i < n ; i++) {{ {type.ElementType.Apply(CsDefineTypeName.Ins)} _e; {type.ElementType.Apply(this, bufName, "_e")} {fieldName}.Add(_e);}}}}"; + } + + public string Accept(TMap type, string bufName, string fieldName) + { + return $"{{int n = System.Math.Min({bufName}.ReadSize(), {bufName}.Size);{fieldName} = new {type.Apply(CsDefineTypeName.Ins)}(n * 3 / 2);for(var i = 0 ; i < n ; i++) {{ {type.KeyType.Apply(CsDefineTypeName.Ins)} _k; {type.KeyType.Apply(this, bufName, "_k")} {type.ValueType.Apply(CsDefineTypeName.Ins)} _v; {type.ValueType.Apply(this, bufName, "_v")} {fieldName}.Add(_k, _v);}}}}"; + + } + + public string Accept(TVector2 type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.ReadVector2();"; + } + + public string Accept(TVector3 type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.ReadVector3();"; + } + + public string Accept(TVector4 type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.ReadVector4();"; + } + + public string Accept(TDateTime type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.ReadInt();"; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/DeepCompareTypeDefine.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/DeepCompareTypeDefine.cs new file mode 100644 index 0000000..adbcd90 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/DeepCompareTypeDefine.cs @@ -0,0 +1,313 @@ +using Luban.Config.Common.RawDefs; +using Luban.Job.Cfg.Defs; +using Luban.Job.Common.Defs; +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System.Collections.Generic; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class DeepCompareTypeDefine : ITypeFuncVisitor<TType, Dictionary<DefTypeBase, bool>, HashSet<DefTypeBase>, bool> + { + public static DeepCompareTypeDefine Ins { get; } = new DeepCompareTypeDefine(); + + private bool IsBaseDefineEqual(DefBean a, DefBean b) + { + if (!a.Name.Equals(b.Name)) + { + return false; + } + if (!a.Namespace.Equals(b.Namespace)) + { + return false; + } + + if (a.Parent != b.Parent) + { + return false; + } + + if (a.IsValueType != b.IsValueType) + { + return false; + } + + if (a.Alias != b.Alias) + { + return false; + } + if (a.IsMultiRow != b.IsMultiRow) + { + return false; + } + if (a.Sep != b.Sep) + { + return false; + } + return true; + } + + public bool Compare(DefBean a, DefBean b, Dictionary<DefTypeBase, bool> ctx, HashSet<DefTypeBase> inWalk) + { + bool setupNotEqual() + { + ctx.Add(a, false); + return false; + } + + bool IsValidatorEquals(List<Validator> vs1, List<Validator> vs2) + { + if (vs1.Count != vs2.Count) + { + return false; + } + for (int i = 0; i < vs1.Count; i++) + { + var v1 = vs1[i]; + var v2 = vs2[i]; + if (v1.Type != v2.Type || v1.Rule != v2.Rule) + { + return false; + } + } + return true; + } + + if (ctx.TryGetValue(a, out var e)) + { + return e; + } + if (inWalk.Contains(a)) + { + return true; + } + + if (!IsBaseDefineEqual(a, b)) + { + return setupNotEqual(); + } + + inWalk.Add(a); + + try + { + if (a.Fields.Count != b.Fields.Count) + { + return setupNotEqual(); + } + + for (int i = 0; i < a.Fields.Count; i++) + { + var f1 = (DefField)a.Fields[i]; + var f2 = (DefField)b.Fields[i]; + if (f1.Name != f2.Name + || f1.NeedExport != f2.NeedExport + || f1.Index != f2.Index + || f1.Sep != f2.Sep + || f1.ResourceTag != f2.ResourceTag + || f1.IsMultiRow != f2.IsMultiRow + || f1.CType.IsNullable != f2.CType.IsNullable + || f1.CType.GetType() != f2.CType.GetType() + || !IsValidatorEquals(f1.RawDefine.Validators, f2.RawDefine.Validators) + || !IsValidatorEquals(f1.RawDefine.KeyValidators, f2.RawDefine.KeyValidators) + || !IsValidatorEquals(f1.RawDefine.ValueValidators, f2.RawDefine.ValueValidators) + ) + { + return setupNotEqual(); + } + + if (!f1.CType.Apply(this, f2.CType, ctx, inWalk)) + { + return setupNotEqual(); + } + } + + + var parentType = (DefBean)a.ParentDefType; + if (parentType != null && !Compare(parentType, (DefBean)b.ParentDefType, ctx, inWalk)) + { + return setupNotEqual(); + } + if (a.Children == null) + { + if (b.Children != null) + { + return setupNotEqual(); + } + } + else + { + if (b.Children == null) + { + return setupNotEqual(); + } + else + { + int index = 0; + foreach (var c in a.Children) + { + if (!Compare((DefBean)c, (DefBean)b.Children[index++], ctx, inWalk)) + { + return setupNotEqual(); + } + } + } + } + + ctx.Add(a, true); + return true; + } + finally + { + //inWalk.Remove(a); + } + } + + public bool Accept(TBool type, TType x, Dictionary<DefTypeBase, bool> y, HashSet<DefTypeBase> z) + { + return true; + } + + public bool Accept(TByte type, TType x, Dictionary<DefTypeBase, bool> y, HashSet<DefTypeBase> z) + { + return true; + } + + public bool Accept(TShort type, TType x, Dictionary<DefTypeBase, bool> y, HashSet<DefTypeBase> z) + { + return true; + } + + public bool Accept(TFshort type, TType x, Dictionary<DefTypeBase, bool> y, HashSet<DefTypeBase> z) + { + return true; + } + + public bool Accept(TInt type, TType x, Dictionary<DefTypeBase, bool> y, HashSet<DefTypeBase> z) + { + return true; + } + + public bool Accept(TFint type, TType x, Dictionary<DefTypeBase, bool> y, HashSet<DefTypeBase> z) + { + return true; + } + + public bool Accept(TLong type, TType x, Dictionary<DefTypeBase, bool> y, HashSet<DefTypeBase> z) + { + return true; + } + + public bool Accept(TFlong type, TType x, Dictionary<DefTypeBase, bool> y, HashSet<DefTypeBase> z) + { + return true; + } + + public bool Accept(TFloat type, TType x, Dictionary<DefTypeBase, bool> y, HashSet<DefTypeBase> z) + { + return true; + } + + public bool Accept(TDouble type, TType x, Dictionary<DefTypeBase, bool> y, HashSet<DefTypeBase> z) + { + return true; + } + + public bool Accept(TEnum type, TType x, Dictionary<DefTypeBase, bool> y, HashSet<DefTypeBase> z) + { + var a = type.DefineEnum; + var b = ((TEnum)x).DefineEnum; + if (y.TryGetValue(a, out var v)) + { + return v; + } + + + var same = a.FullName == b.FullName + && a.IsFlags == b.IsFlags + && a.IsUniqueItemId == b.IsUniqueItemId + && IsEnumItemEquals(a.Items, b.Items); + y.Add(a, same); + return same; + } + + private bool IsEnumItemEquals(List<DefEnum.Item> a, List<DefEnum.Item> b) + { + if (a.Count != b.Count) + { + return false; + } + for (int i = 0; i < a.Count; i++) + { + var ia = a[i]; + var ib = b[i]; + if (ia.Name != ib.Name || ia.Value != ib.Value || ia.Alias != ib.Alias) + { + return false; + } + } + return true; + } + + public bool Accept(TString type, TType x, Dictionary<DefTypeBase, bool> y, HashSet<DefTypeBase> z) + { + return true; + } + + public bool Accept(TBytes type, TType x, Dictionary<DefTypeBase, bool> y, HashSet<DefTypeBase> z) + { + return true; + } + + public bool Accept(TText type, TType x, Dictionary<DefTypeBase, bool> y, HashSet<DefTypeBase> z) + { + return true; + } + + public bool Accept(TBean type, TType x, Dictionary<DefTypeBase, bool> y, HashSet<DefTypeBase> z) + { + return Compare(type.GetBeanAs<DefBean>(), ((TBean)x).GetBeanAs<DefBean>(), y, z); + } + + public bool Accept(TArray type, TType x, Dictionary<DefTypeBase, bool> y, HashSet<DefTypeBase> z) + { + return type.ElementType.Apply(this, ((TArray)x).ElementType, y, z); + } + + public bool Accept(TList type, TType x, Dictionary<DefTypeBase, bool> y, HashSet<DefTypeBase> z) + { + return type.ElementType.Apply(this, ((TList)x).ElementType, y, z); + } + + public bool Accept(TSet type, TType x, Dictionary<DefTypeBase, bool> y, HashSet<DefTypeBase> z) + { + return type.ElementType.Apply(this, ((TSet)x).ElementType, y, z); + } + + public bool Accept(TMap type, TType x, Dictionary<DefTypeBase, bool> y, HashSet<DefTypeBase> z) + { + TMap m = (TMap)x; + return type.KeyType.Apply(this, m.KeyType, y, z) && type.ValueType.Apply(this, m.ValueType, y, z); + } + + public bool Accept(TVector2 type, TType x, Dictionary<DefTypeBase, bool> y, HashSet<DefTypeBase> z) + { + return true; + } + + public bool Accept(TVector3 type, TType x, Dictionary<DefTypeBase, bool> y, HashSet<DefTypeBase> z) + { + return true; + } + + public bool Accept(TVector4 type, TType x, Dictionary<DefTypeBase, bool> y, HashSet<DefTypeBase> z) + { + return true; + } + + public bool Accept(TDateTime type, TType x, Dictionary<DefTypeBase, bool> y, HashSet<DefTypeBase> z) + { + return true; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/EditorUeCppDefineTypeVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/EditorUeCppDefineTypeVisitor.cs new file mode 100644 index 0000000..4250c3d --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/EditorUeCppDefineTypeVisitor.cs @@ -0,0 +1,25 @@ +using Luban.Job.Common.Types; +using System; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class EditorUeCppDefineTypeVisitor : UeBpCppDefineTypeVisitor + { + public static new EditorUeCppDefineTypeVisitor Ins { get; } = new EditorUeCppDefineTypeVisitor(); + + + public override string Accept(TEnum type) + { + //return type.DefineEnum.UeFfullName; + throw new NotImplementedException(); + } + + + public override string Accept(TBean type) + { + //return $"TSharedPtr<{type.Bean.UeFfullName}>"; + throw new NotImplementedException(); + } + + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/EditorUeCppLoadVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/EditorUeCppLoadVisitor.cs new file mode 100644 index 0000000..48a751b --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/EditorUeCppLoadVisitor.cs @@ -0,0 +1,184 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class EditorUeCppLoadVisitor : ITypeFuncVisitor<string, string, string> + { + public static EditorUeCppLoadVisitor Ins { get; } = new EditorUeCppLoadVisitor(); + + + private string DefaultLoad(string json, string field) + { + return $"if (!JsonUtil::Read({json}, \"{field}\", this->{field})) {{ return false; }}"; + } + + public string Accept(TBool type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TByte type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TShort type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TFshort type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TInt type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TFint type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TLong type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TFlong type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TFloat type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TDouble type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TEnum type, string json, string field) + { + //return $"{{FString _enumStr_; if(!JsonUtil::Read({json}, \"{field}\", _enumStr_)) {{ return false; }} if(!{type.DefineEnum.UeFname}FromString(_enumStr_, this->{field})) {{ return false;}} }}"; + throw new NotImplementedException(); + } + + public string Accept(TString type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TBytes type, string json, string field) + { + throw new NotImplementedException(); + } + + public string Accept(TText type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TBean type, string json, string field) + { + if (type.Bean.IsAbstractType) + { + return $"if(!JsonUtil::ReadDynamicBean({json}, \"{field}\", this->{field})){{ return false; }}"; + } + else + { + return $"if(!JsonUtil::ReadBean({json}, \"{field}\", this->{field})) {{ return false; }}"; + } + } + + private string LoadList(TType elementType, string json, string field) + { + return $@" + {{ + const TArray<TSharedPtr<FJsonValue>>* _parr; + if ({json}->TryGetArrayField(""{field}"", _parr)) + {{ + for (const TSharedPtr<FJsonValue>& e : *_parr) + {{ + {elementType.Apply(EditorUeCppDefineTypeVisitor.Ins)} _v; + {elementType.Apply(EditorUeCppLoadVisitor2.Ins, "e.Get()", "_v")} + this->{field}.Add(_v); + }} + }} + else + {{ + return false; + }} + }} +"; + } + + public string Accept(TArray type, string json, string field) + { + return LoadList(type.ElementType, json, field); + } + + public string Accept(TList type, string json, string field) + { + return LoadList(type.ElementType, json, field); + } + + public string Accept(TSet type, string json, string field) + { + return LoadList(type.ElementType, json, field); + } + + public string Accept(TMap type, string json, string field) + { + return $@" + {{ + const TArray<TSharedPtr<FJsonValue>>* _parr; + if ({json}->TryGetArrayField(""{field}"", _parr)) + {{ + for (const TSharedPtr<FJsonValue>& e : *_parr) + {{ + const TArray<TSharedPtr<FJsonValue>>* _pentry; + if (!e->TryGetArray(_pentry) || _pentry->Num() != 2) return false; + + {type.KeyType.Apply(EditorUeCppDefineTypeVisitor.Ins)} _k; + {type.KeyType.Apply(EditorUeCppLoadVisitor2.Ins, "(*_pentry)[0].Get()", "_k")} + {type.ValueType.Apply(EditorUeCppDefineTypeVisitor.Ins)} _v; + {type.ValueType.Apply(EditorUeCppLoadVisitor2.Ins, "(*_pentry)[1].Get()", "_v")} + this->{field}.Add(_k, _v); + }} + }} + else + {{ + return false; + }} + }} +"; + } + + public string Accept(TVector2 type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TVector3 type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TVector4 type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TDateTime type, string json, string field) + { + return DefaultLoad(json, field); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/EditorUeCppLoadVisitor2.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/EditorUeCppLoadVisitor2.cs new file mode 100644 index 0000000..dde6689 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/EditorUeCppLoadVisitor2.cs @@ -0,0 +1,142 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class EditorUeCppLoadVisitor2 : ITypeFuncVisitor<string, string, string> + { + public static EditorUeCppLoadVisitor2 Ins { get; } = new EditorUeCppLoadVisitor2(); + + + private string DefaultLoad(string json, string field) + { + return $"if(!JsonUtil::Read({json}, {field})) return false;"; + } + + public string Accept(TBool type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TByte type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TShort type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TFshort type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TInt type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TFint type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TLong type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TFlong type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TFloat type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TDouble type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TEnum type, string json, string field) + { + //return $"{{FString _enumStr_; if(!JsonUtil::Read({json}, _enumStr_)) {{ return false; }} if(!{type.DefineEnum.UeFname}FromString(_enumStr_, {field})) {{ return false;}} }}"; + throw new NotImplementedException(); + } + + public string Accept(TString type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TBytes type, string json, string field) + { + throw new NotImplementedException(); + } + + public string Accept(TText type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TBean type, string json, string field) + { + if (type.Bean.IsAbstractType) + { + return $"if (!JsonUtil::ReadDynamicBean({json}, {field})) return false;"; + } + else + { + return $"if (!JsonUtil::ReadBean({json}, {field})) return false;"; + } + } + + public string Accept(TArray type, string json, string field) + { + return ""; + } + + public string Accept(TList type, string json, string field) + { + + return ""; + } + + public string Accept(TSet type, string json, string field) + { + + return ""; + } + + public string Accept(TMap type, string json, string field) + { + return ""; + } + + public string Accept(TVector2 type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TVector3 type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TVector4 type, string json, string field) + { + return DefaultLoad(json, field); + } + + public string Accept(TDateTime type, string json, string field) + { + return DefaultLoad(json, field); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/EditorUeCppSaveVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/EditorUeCppSaveVisitor.cs new file mode 100644 index 0000000..2c22eb4 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/EditorUeCppSaveVisitor.cs @@ -0,0 +1,176 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class EditorUeCppSaveVisitor : ITypeFuncVisitor<string, string, string> + { + + public static EditorUeCppSaveVisitor Ins { get; } = new EditorUeCppSaveVisitor(); + + private string DefaultSave(string json, string field) + { + return $"if (!JsonUtil::Write({json}, \"{field}\", this->{field})) {{ delete {json}; return false; }}"; + } + + public string Accept(TBool type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TByte type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TShort type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TFshort type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TInt type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TFint type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TLong type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TFlong type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TFloat type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TDouble type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TEnum type, string json, string field) + { + //return $"{{FString _enumStr_; if(!{type.DefineEnum.UeFname}ToString(this->{field}, _enumStr_)) {{ return false;}} if(!JsonUtil::Write({json}, \"{field}\", _enumStr_)) {{ return false;}} }}"; + throw new NotImplementedException(); + } + + public string Accept(TString type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TBytes type, string json, string field) + { + throw new NotImplementedException(); + } + + public string Accept(TText type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TBean type, string json, string field) + { + if (type.Bean.IsAbstractType) + { + + return $"if (!JsonUtil::WriteDynamicBean({json}, \"{field}\", this->{field})) {{ delete {json}; return false; }}"; + } + else + { + + return $"if (!JsonUtil::WriteBean({json}, \"{field}\", this->{field})) {{ delete {json}; return false; }}"; + } + } + + private string SaveList(TType elementType, string json, string field) + { + return $@" + {{ + TArray<TSharedPtr<FJsonValue>> _arr; + for (auto _v : this->{field}) + {{ + {elementType.Apply(EditorUeCppTypeJsonValueTypeNameVisitor.Ins)}* _vj = nullptr; + {elementType.Apply(EditorUeCppSaveVisitor2.Ins, "_vj", "_v")} + _arr.Add(TSharedPtr<FJsonValue>(_vj)); + }} + {json}->SetArrayField(""{field}"", _arr); + }} +"; + } + + public string Accept(TArray type, string json, string field) + { + return SaveList(type.ElementType, json, field); + } + + public string Accept(TList type, string json, string field) + { + return SaveList(type.ElementType, json, field); + } + + public string Accept(TSet type, string json, string field) + { + return SaveList(type.ElementType, json, field); + } + + public string Accept(TMap type, string json, string field) + { + return $@" + {{ + TArray<TSharedPtr<FJsonValue>> _arr; + for (auto& _e : this->{field}) + {{ + TArray<TSharedPtr<FJsonValue>> _entry; + {type.KeyType.Apply(EditorUeCppTypeJsonValueTypeNameVisitor.Ins)}* _kj = nullptr; + {type.KeyType.Apply(EditorUeCppSaveVisitor2.Ins, "_kj", "_e.Key")} + _entry.Add(TSharedPtr<FJsonValue>(_kj)); + + {type.ValueType.Apply(EditorUeCppTypeJsonValueTypeNameVisitor.Ins)}* _vj = nullptr; + {type.ValueType.Apply(EditorUeCppSaveVisitor2.Ins, "_vj", "_e.Value")} + _entry.Add(TSharedPtr<FJsonValue>(_vj)); + + _arr.Add(TSharedPtr<FJsonValue>(new FJsonValueArray(_entry))); + }} + {json}->SetArrayField(""{field}"", _arr); + }} +"; + } + + public string Accept(TVector2 type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TVector3 type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TVector4 type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TDateTime type, string json, string field) + { + return DefaultSave(json, field); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/EditorUeCppSaveVisitor2.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/EditorUeCppSaveVisitor2.cs new file mode 100644 index 0000000..b789d8a --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/EditorUeCppSaveVisitor2.cs @@ -0,0 +1,142 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class EditorUeCppSaveVisitor2 : ITypeFuncVisitor<string, string, string> + { + + public static EditorUeCppSaveVisitor2 Ins { get; } = new EditorUeCppSaveVisitor2(); + + private string DefaultSave(string json, string field) + { + return $"if(!JsonUtil::Write({json}, {field})) {{ return false;}}"; + } + + public string Accept(TBool type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TByte type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TShort type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TFshort type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TInt type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TFint type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TLong type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TFlong type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TFloat type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TDouble type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TEnum type, string json, string field) + { + //return $"{{FString _enumStr_; if(!{type.DefineEnum.UeFname}ToString({field}, _enumStr_)) {{ return false;}} if(!JsonUtil::Write({json}, _enumStr_)) {{ return false;}} }}"; + throw new NotImplementedException(); + } + + public string Accept(TString type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TBytes type, string json, string field) + { + throw new NotImplementedException(); + } + + public string Accept(TText type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TBean type, string json, string field) + { + if (type.Bean.IsAbstractType) + { + + return $"if(!JsonUtil::WriteDynamicBean({json}, {field})) {{ return false; }}"; + } + else + { + + return $"if(!JsonUtil::WriteBean({json}, {field})) {{ return false; }}"; + } + } + + public string Accept(TArray type, string json, string field) + { + return ""; + } + + public string Accept(TList type, string json, string field) + { + return ""; + } + + public string Accept(TSet type, string json, string field) + { + return ""; + } + + public string Accept(TMap type, string json, string field) + { + return ""; + } + + public string Accept(TVector2 type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TVector3 type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TVector4 type, string json, string field) + { + return DefaultSave(json, field); + } + + public string Accept(TDateTime type, string json, string field) + { + return DefaultSave(json, field); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/EditorUeCppTypeJsonValueTypeNameVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/EditorUeCppTypeJsonValueTypeNameVisitor.cs new file mode 100644 index 0000000..ac06c12 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/EditorUeCppTypeJsonValueTypeNameVisitor.cs @@ -0,0 +1,126 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class EditorUeCppTypeJsonValueTypeNameVisitor : ITypeFuncVisitor<string> + { + public static EditorUeCppTypeJsonValueTypeNameVisitor Ins { get; } = new EditorUeCppTypeJsonValueTypeNameVisitor(); + + public string Accept(TBool type) + { + return "FJsonValueBoolean"; + } + + public string Accept(TByte type) + { + return "FJsonValueNumber"; + } + + public string Accept(TShort type) + { + return "FJsonValueNumber"; + } + + public string Accept(TFshort type) + { + return "FJsonValueNumber"; + } + + public string Accept(TInt type) + { + return "FJsonValueNumber"; + } + + public string Accept(TFint type) + { + return "FJsonValueNumber"; + } + + public string Accept(TLong type) + { + return "FJsonValueNumber"; + } + + public string Accept(TFlong type) + { + return "FJsonValueNumber"; + } + + public string Accept(TFloat type) + { + return "FJsonValueNumber"; + } + + public string Accept(TDouble type) + { + return "FJsonValueNumber"; + } + + public string Accept(TEnum type) + { + return "FJsonValueString"; + } + + public string Accept(TString type) + { + return "FJsonValueString"; + } + + public string Accept(TBytes type) + { + throw new NotImplementedException(); + } + + public string Accept(TText type) + { + return "FJsonValueString"; + } + + public string Accept(TBean type) + { + return "FJsonValueObject"; + } + + public string Accept(TArray type) + { + return "FJsonValueArray"; + } + + public string Accept(TList type) + { + return "FJsonValueArray"; + } + + public string Accept(TSet type) + { + return "FJsonValueArray"; + } + + public string Accept(TMap type) + { + return "FJsonValueObject"; + } + + public string Accept(TVector2 type) + { + return "FJsonValueObject"; + } + + public string Accept(TVector3 type) + { + return "FJsonValueObject"; + } + + public string Accept(TVector4 type) + { + return "FJsonValueObject"; + } + + public string Accept(TDateTime type) + { + return "FJsonValueNumber"; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/ExcelDataCreator.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/ExcelDataCreator.cs new file mode 100644 index 0000000..c329e0d --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/ExcelDataCreator.cs @@ -0,0 +1,403 @@ +using Luban.Common.Utils; +using Luban.Job.Cfg.Datas; +using Luban.Job.Cfg.DataSources.Excel; +using Luban.Job.Cfg.Defs; +using Luban.Job.Cfg.Utils; +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System; +using System.Collections.Generic; + +namespace Luban.Job.Cfg.TypeVisitors +{ + enum EReadPolicy + { + SKIP_NULL_CELL = 0x1, + SKIP_BLANK_CELL = 0x2, + NULL_AS_NULL = 0x4, + NULL_STR_AS_NULL = 0x8, + } + + class ExcelDataCreator : ITypeFuncVisitor<object, ExcelStream, DefAssembly, DType> + { + public static ExcelDataCreator Ins { get; } = new ExcelDataCreator(); + + private bool CheckNull(bool nullable, object o) + { + if (o is string s && s == "null") + { + if (nullable) + { + return true; + } + else + { + throw new Exception($"单元格没有填有效数据"); + } + } + return false; + } + + private static bool CreateBool(object x) + { + if (x is bool b) + { + return b; + } + var s = x.ToString().ToLower().Trim(); + switch (s) + { + case "true": + case "是": return true; + case "false": + case "否": return false; + default: throw new Exception($"{s} 不是 bool 类型的值 (true 或 false)"); + } + } + + public DType Accept(TBool type, object converter, ExcelStream x, DefAssembly ass) + { + var d = x.Read(); + if (CheckNull(type.IsNullable, d)) + { + return null; + } + return new DBool(CreateBool(d)); + } + + public DType Accept(TByte type, object converter, ExcelStream x, DefAssembly ass) + { + var d = x.Read(); + if (CheckNull(type.IsNullable, d)) + { + return null; + } + if (!byte.TryParse(d.ToString(), out byte v)) + { + throw new Exception($"{d} 不是 byte 类型值"); + } + return new DByte(v); + } + + public DType Accept(TShort type, object converter, ExcelStream x, DefAssembly ass) + { + var d = x.Read(); + if (CheckNull(type.IsNullable, d)) + { + return null; + } + if (!short.TryParse(d.ToString(), out short v)) + { + throw new Exception($"{d} 不是 short 类型值"); + } + return new DShort(v); + } + + public DType Accept(TFshort type, object converter, ExcelStream x, DefAssembly ass) + { + var d = x.Read(); + if (CheckNull(type.IsNullable, d)) + { + return null; + } + if (!short.TryParse(d.ToString(), out short v)) + { + throw new Exception($"{d} 不是 short 类型值"); + } + return new DFshort(v); + } + + public DType Accept(TInt type, object converter, ExcelStream x, DefAssembly ass) + { + var d = x.Read(); + if (CheckNull(type.IsNullable, d)) + { + return null; + } + var ds = d.ToString(); + if (converter is TEnum te) + { + if (te.DefineEnum.TryValueByNameOrAlias(ds, out var c)) + { + return new DInt(c); + } + } + if (!int.TryParse(ds, out var v)) + { + throw new Exception($"{d} 不是 int 类型值"); + } + return new DInt(v); + } + + public DType Accept(TFint type, object converter, ExcelStream x, DefAssembly ass) + { + var d = x.Read(); + if (CheckNull(type.IsNullable, d)) + { + return null; + } + var ds = d.ToString(); + if (converter is TEnum te) + { + if (te.DefineEnum.TryValueByNameOrAlias(ds, out var c)) + { + return new DFint(c); + } + } + if (!int.TryParse(ds, out var v)) + { + throw new Exception($"{d} 不是 int 类型值"); + } + return new DFint(v); + } + + public DType Accept(TLong type, object converter, ExcelStream x, DefAssembly ass) + { + var d = x.Read(); + if (CheckNull(type.IsNullable, d)) + { + return null; + } + var ds = d.ToString(); + if (converter is TEnum te) + { + if (te.DefineEnum.TryValueByNameOrAlias(ds, out var c)) + { + return new DLong(c); + } + } + if (!long.TryParse(ds, out var v)) + { + throw new Exception($"{d} 不是 long 类型值"); + } + return new DLong(v); + } + + public DType Accept(TFlong type, object converter, ExcelStream x, DefAssembly ass) + { + var d = x.Read(); + if (CheckNull(type.IsNullable, d)) + { + return null; + } + var ds = d.ToString(); + if (converter is TEnum te) + { + if (te.DefineEnum.TryValueByNameOrAlias(ds, out var c)) + { + return new DFlong(c); + } + } + if (!long.TryParse(ds, out var v)) + { + throw new Exception($"{d} 不是 long 类型值"); + } + return new DFlong(v); + } + + public DType Accept(TFloat type, object converter, ExcelStream x, DefAssembly ass) + { + var d = x.Read(); + if (CheckNull(type.IsNullable, d)) + { + return null; + } + if (!float.TryParse(d.ToString(), out var v)) + { + throw new Exception($"{d} 不是 float 类型值"); + } + return new DFloat(v); + } + + public DType Accept(TDouble type, object converter, ExcelStream x, DefAssembly ass) + { + var d = x.Read(); + if (CheckNull(type.IsNullable, d)) + { + return null; + } + if (!double.TryParse(d.ToString(), out var v)) + { + throw new Exception($"{d} 不是 double 类型值"); + } + return new DDouble(v); + } + + public DType Accept(TEnum type, object converter, ExcelStream x, DefAssembly ass) + { + var d = x.Read(); + if (CheckNull(type.IsNullable, d)) + { + return null; + } + return new DEnum(type, d.ToString().Trim()); + } + + public DType Accept(TString type, object converter, ExcelStream x, DefAssembly ass) + { + var d = x.Read(); + if (d is string s && s == "null") + { + return new DString(""); + } + return new DString(d.ToString()); + } + + public DType Accept(TBytes type, object converter, ExcelStream x, DefAssembly ass) + { + throw new NotImplementedException(); + } + + public DType Accept(TText type, object converter, ExcelStream x, DefAssembly ass) + { + var d = x.Read(); + if (d is string s && s == "null") + { + return new DString(""); + } + return new DString(d.ToString()); + } + + private List<DType> CreateBeanFields(DefBean bean, ExcelStream stream, DefAssembly ass) + { + var list = new List<DType>(); + foreach (DefField f in bean.HierarchyFields) + { + try + { + string sep = f.ActualSep; + if (string.IsNullOrWhiteSpace(sep)) + { + list.Add(f.CType.Apply(this, f.Remapper, stream, ass)); + } + else + { + list.Add(f.CType.Apply(this, f.Remapper, new ExcelStream(stream.ReadCell(), sep), ass)); + } + } + catch (Exception e) + { + throw new Exception($"读取结构:{bean.FullName} 字段:{f.Name} 出错 ==> {e.Message}", e); + } + } + return list; + } + + public DType Accept(TBean type, object converter, ExcelStream x, DefAssembly ass) + { + var originBean = (DefBean)type.Bean; + + if (originBean.IsAbstractType) + { + string subType = x.Read().ToString(); + if (subType.ToLower().Trim() == "null") + { + return new DBean(originBean, null, null); + } + string fullType = TypeUtil.MakeFullName(originBean.Namespace, subType); + DefBean implType = (DefBean)originBean.GetNotAbstractChildType(subType); + if (implType == null) + { + throw new Exception($"type:{fullType} 不是bean类型"); + } + return new DBean(originBean, implType, CreateBeanFields(implType, x, ass)); + } + else + { + return new DBean(originBean, originBean, CreateBeanFields(originBean, x, ass)); + } + } + + // 容器类统统不支持 type.IsNullable + // 因为貌似没意义? + public List<DType> ReadList(TType type, object converter, ExcelStream stream, DefAssembly ass) + { + string sep = type is TBean bean ? ((DefBean)bean.Bean).Sep : null; + var datas = new List<DType>(); + while (!stream.TryReadEOF()) + { + if (string.IsNullOrWhiteSpace(sep)) + { + datas.Add(type.Apply(this, converter, stream, ass)); + } + else + { + datas.Add(type.Apply(this, converter, new ExcelStream(stream.ReadCell(), sep), ass)); + } + } + return datas; + } + + public DType Accept(TArray type, object converter, ExcelStream x, DefAssembly ass) + { + return new DArray(type, ReadList(type.ElementType, converter, x, ass)); + } + + public DType Accept(TList type, object converter, ExcelStream x, DefAssembly ass) + { + return new DList(type, ReadList(type.ElementType, converter, x, ass)); + } + + public DType Accept(TSet type, object converter, ExcelStream x, DefAssembly ass) + { + return new DSet(type, ReadList(type.ElementType, converter, x, ass)); + } + + public DType Accept(TMap type, object converter, ExcelStream x, DefAssembly ass) + { + string sep = type.ValueType is TBean bean ? ((DefBean)bean.Bean).Sep : null; + + var datas = new Dictionary<DType, DType>(); + while (!x.TryReadEOF()) + { + var key = type.KeyType.Apply(this, null, x, ass); + var value = string.IsNullOrWhiteSpace(sep) ? type.ValueType.Apply(this, null, x, ass) : type.ValueType.Apply(this, null, new ExcelStream(x.ReadCell(), sep), ass); + if (!datas.TryAdd(key, value)) + { + throw new Exception($"map 的 key:{key} 重复"); + } + } + return new DMap(type, datas); + } + + public DType Accept(TVector2 type, object converter, ExcelStream x, DefAssembly ass) + { + var d = x.Read(); + if (CheckNull(type.IsNullable, d)) + { + return null; + } + return DataUtil.CreateVector(type, d.ToString()); + } + + public DType Accept(TVector3 type, object converter, ExcelStream x, DefAssembly ass) + { + var d = x.Read(); + if (CheckNull(type.IsNullable, d)) + { + return null; + } + return DataUtil.CreateVector(type, d.ToString()); + } + + public DType Accept(TVector4 type, object converter, ExcelStream x, DefAssembly ass) + { + var d = x.Read(); + if (CheckNull(type.IsNullable, d)) + { + return null; + } + return DataUtil.CreateVector(type, d.ToString()); + } + + public DType Accept(TDateTime type, object converter, ExcelStream x, DefAssembly ass) + { + var d = x.Read(); + if (CheckNull(type.IsNullable, d)) + { + return null; + } + return DataUtil.CreateDateTime(d.ToString(), ass.TimeZone); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/ExcelNamedRowDataCreator.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/ExcelNamedRowDataCreator.cs new file mode 100644 index 0000000..da12db2 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/ExcelNamedRowDataCreator.cs @@ -0,0 +1,273 @@ +using Luban.Common.Utils; +using Luban.Job.Cfg.Datas; +using Luban.Job.Cfg.DataSources.Excel; +using Luban.Job.Cfg.Defs; +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System; +using System.Collections.Generic; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class ExcelNamedRowDataCreator : ITypeFuncVisitor<Sheet.NamedRow, bool, bool, DType> + { + public static ExcelNamedRowDataCreator Ins { get; } = new ExcelNamedRowDataCreator(); + + + public DType ReadExcel(Sheet.NamedRow row, TBean btype) + { + return Accept(btype, row, false, false); + } + + public DType Accept(TBool type, Sheet.NamedRow x, bool multirow, bool nullable) + { + throw new NotSupportedException(); + } + + public DType Accept(TByte type, Sheet.NamedRow x, bool multirow, bool nullable) + { + throw new NotSupportedException(); + } + + public DType Accept(TShort type, Sheet.NamedRow x, bool multirow, bool nullable) + { + throw new NotSupportedException(); + } + + public DType Accept(TFshort type, Sheet.NamedRow x, bool multirow, bool nullable) + { + throw new NotSupportedException(); + } + + public DType Accept(TInt type, Sheet.NamedRow x, bool multirow, bool nullable) + { + throw new NotSupportedException(); + } + + public DType Accept(TFint type, Sheet.NamedRow x, bool multirow, bool nullable) + { + throw new NotSupportedException(); + } + + public DType Accept(TLong type, Sheet.NamedRow x, bool multirow, bool nullable) + { + throw new NotSupportedException(); + } + + public DType Accept(TFlong type, Sheet.NamedRow x, bool multirow, bool nullable) + { + throw new NotSupportedException(); + } + + public DType Accept(TFloat type, Sheet.NamedRow x, bool multirow, bool nullable) + { + throw new NotSupportedException(); + } + + public DType Accept(TDouble type, Sheet.NamedRow x, bool multirow, bool nullable) + { + throw new NotSupportedException(); + } + + public DType Accept(TEnum type, Sheet.NamedRow x, bool multirow, bool nullable) + { + throw new NotSupportedException(); + } + + public DType Accept(TString type, Sheet.NamedRow x, bool multirow, bool nullable) + { + throw new NotSupportedException(); + } + + public DType Accept(TBytes type, Sheet.NamedRow x, bool multirow, bool nullable) + { + throw new NotSupportedException(); + } + + public DType Accept(TText type, Sheet.NamedRow x, bool multirow, bool nullable) + { + throw new NotSupportedException(); + } + + public bool IsContainerAndElementNotSepType(TType type) + { + switch (type) + { + case TArray ta: return ta.ElementType.Apply(IsNotSepTypeVisitor.Ins); + case TList tl: return tl.ElementType.Apply(IsNotSepTypeVisitor.Ins); + case TSet ts: return ts.ElementType.Apply(IsNotSepTypeVisitor.Ins); + case TMap tm: return tm.KeyType.Apply(IsNotSepTypeVisitor.Ins) && tm.ValueType.Apply(IsNotSepTypeVisitor.Ins); + default: return false; + } + throw new NotImplementedException(); + } + + private List<DType> CreateBeanFields(DefBean bean, Sheet.NamedRow row) + { + var list = new List<DType>(); + foreach (DefField f in bean.HierarchyFields) + { + string fname = f.Name; + Sheet.Title title = row.GetTitle(fname); + if (title == null) + { + throw new Exception($"bean:{bean.FullName} 缺失 列:{fname},请检查是否写错或者遗漏"); + } + // 多级标题 + if (title.SubTitles.Count > 0) + { + try + { + if (f.IsMultiRow) + { + list.Add(f.CType.Apply(this, row.GetSubTitleNamedRowOfMultiRows(fname), f.RawIsMultiRow, f.IsNullable)); + } + else + { + list.Add(f.CType.Apply(this, row.GetSubTitleNamedRow(fname), f.RawIsMultiRow /* 肯定是 false */, f.IsNullable)); + } + } + catch (Exception e) + { + throw new Exception($"读取结构:{bean.FullName} 字段:{fname} 读取 出错 ==> {e.Message}", e); + } + } + else + { + string sep = f.ActualSep; + if (string.IsNullOrWhiteSpace(sep) && IsContainerAndElementNotSepType(f.CType)) + { + sep = ";,"; + } + + if (f.IsMultiRow) + { + try + { + list.Add(f.CType.Apply(MultiRowExcelDataCreator.Ins, row.GetColumnOfMultiRows(f.Name, sep), f.IsNullable, (DefAssembly)bean.AssemblyBase)); + } + catch (Exception e) + { + throw new Exception($"读取结构:{bean.FullName} 多行字段:{f.Name} 读取 出错 ==> {e.Message}", e); + } + } + else + { + ExcelStream stream = row.GetColumn(f.Name, sep); + try + { + list.Add(f.CType.Apply(ExcelDataCreator.Ins, f.Remapper, stream, (DefAssembly)bean.AssemblyBase)); + } + catch (Exception e) + { + throw new Exception($"读取结构:{bean.FullName} 字段:{f.Name} 位置:{stream.CurrentExcelPosition} 出错 ==> {e.Message}", e); + } + } + } + } + return list; + } + + public DType Accept(TBean type, Sheet.NamedRow row, bool multirow, bool nullable) + { + var originBean = (DefBean)type.Bean; + if (originBean.IsAbstractType) + { + string subType = row.GetColumn(DefBean.TYPE_NAME_KEY, null).Read().ToString().Trim(); + if (subType.ToLower() == "null") + { + return new DBean(originBean, null, null); + } + string fullType = TypeUtil.MakeFullName(originBean.Namespace, subType); + DefBean implType = (DefBean)originBean.GetNotAbstractChildType(subType); + if (implType == null) + { + throw new Exception($"type:{fullType} 不是 bean 类型"); + } + return new DBean(originBean, implType, CreateBeanFields(implType, row)); + } + else + { + return new DBean(originBean, originBean, CreateBeanFields(originBean, row)); + } + } + + + private List<DType> ReadList(TBean elementType, Sheet.NamedRow row, bool multirow) + { + var list = new List<DType>(); + // 如果是多行数据,以当前title为title,每行读入一个element + if (multirow) + { + foreach (var sub in row.GenerateSubNameRows()) + { + list.Add(this.Accept(elementType, sub, false, false)); + } + } + else + { + // 如果不是多行,并且定义了子标题的话。以一个子标题所占的列,读入一个数据 + + foreach (var sub in row.SelfTitle.SubTitleList) + { + list.Add(this.Accept(elementType, new Sheet.NamedRow(sub, row.Rows), false, false)); + } + } + return list; + } + + public DType Accept(TArray type, Sheet.NamedRow x, bool multirow, bool nullable) + { + if (!(type.ElementType is TBean bean)) + { + throw new Exception($"NamedRow 只支持 bean 类型的容器"); + } + else + { + return new DArray(type, ReadList(bean, x, multirow)); + } + } + + public DType Accept(TList type, Sheet.NamedRow x, bool multirow, bool nullable) + { + if (!(type.ElementType is TBean bean)) + { + throw new Exception($"NamedRow 只支持 bean 类型的容器"); + } + else + { + return new DList(type, ReadList(bean, x, multirow)); + } + } + + public DType Accept(TSet type, Sheet.NamedRow x, bool multirow, bool nullable) + { + throw new NotSupportedException(); + } + + public DType Accept(TMap type, Sheet.NamedRow x, bool multirow, bool nullable) + { + throw new NotSupportedException(); + } + + public DType Accept(TVector2 type, Sheet.NamedRow x, bool multirow, bool nullable) + { + throw new NotSupportedException(); + } + + public DType Accept(TVector3 type, Sheet.NamedRow x, bool multirow, bool nullable) + { + throw new NotSupportedException(); + } + + public DType Accept(TVector4 type, Sheet.NamedRow x, bool multirow, bool nullable) + { + throw new NotSupportedException(); + } + + public DType Accept(TDateTime type, Sheet.NamedRow x, bool multirow, bool nullable) + { + throw new NotSupportedException(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/GoDeserializeVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/GoDeserializeVisitor.cs new file mode 100644 index 0000000..a719054 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/GoDeserializeVisitor.cs @@ -0,0 +1,153 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class GoDeserializeVisitor : ITypeFuncVisitor<string, string> + { + public static GoDeserializeVisitor Ins { get; } = new GoDeserializeVisitor(); + + public string Accept(TBool type, string bufName) + { + return $"{bufName}.ReadBool()"; + } + + public string Accept(TByte type, string bufName) + { + return $"{bufName}.ReadByte()"; + } + + public string Accept(TShort type, string bufName) + { + return $"{bufName}.ReadShort()"; + } + + public string Accept(TFshort type, string bufName) + { + return $"{bufName}.ReadFshort()"; + } + + public string Accept(TInt type, string bufName) + { + return $"{bufName}.ReadInt()"; + } + + public string Accept(TFint type, string bufName) + { + return $"{bufName}.ReadFint()"; + } + + public string Accept(TLong type, string bufName) + { + return $"{bufName}.ReadLong()"; + } + + public string Accept(TFlong type, string bufName) + { + return $"{bufName}.ReadFlong()"; + } + + public string Accept(TFloat type, string bufName) + { + return $"{bufName}.ReadFloat()"; + } + + public string Accept(TDouble type, string bufName) + { + return $"{bufName}.ReadDouble()"; + } + + public string Accept(TEnum type, string bufName) + { + return $"{bufName}.ReadInt()"; + } + + public string Accept(TString type, string bufName) + { + return $"{bufName}.ReadString()"; + } + + public string Accept(TBytes type, string bufName) + { + return $"{bufName}.ReadBytes()"; + } + + public string Accept(TText type, string bufName) + { + return $"{bufName}.ReadString()"; + } + + public string Accept(TBean type, string bufName) + { + return type.Bean.IsAbstractType ? $"NewChild{type.Bean.GoFullName}({bufName})" : $"New{ type.Bean.GoFullName} ({ bufName})"; + } + + + private string GenList(TType elementType, string bufName) + { + return $@"func (_buf2 *serialization.ByteBuf) (_v2 []{elementType.Apply(GoTypeNameVisitor.Ins)}, err2 error) {{ + _v2 = make([]{elementType.Apply(GoTypeNameVisitor.Ins)}, 0) + var n int + if n, err2 = _buf2.ReadSize(); err2 != nil {{return}} + for i := 0 ; i < n ; i++ {{ + var v3 {elementType.Apply(GoTypeNameVisitor.Ins)} + if v3, err2 = {elementType.Apply(this, "_buf2")}; err2 != nil {{return}} + _v2 = append(_v2, v3) + }} + return + }}({bufName})"; + } + + public string Accept(TArray type, string bufName) + { + return GenList(type.ElementType, bufName); + } + + public string Accept(TList type, string bufName) + { + return GenList(type.ElementType, bufName); + } + + public string Accept(TSet type, string bufName) + { + return GenList(type.ElementType, bufName); + } + + public string Accept(TMap type, string bufName) + { + return $@"func (_buf2 *serialization.ByteBuf) (_v2 {type.Apply(GoTypeNameVisitor.Ins)}, err2 error) {{ + _v2 = make({type.Apply(GoTypeNameVisitor.Ins)}) + var n int + if n, err2 = _buf2.ReadSize(); err2 != nil {{return}} + for i := 0 ; i < n ; i++ {{ + var _key {type.KeyType.Apply(GoTypeNameVisitor.Ins)} + if _key, err2 = {type.KeyType.Apply(this, "_buf2")}; err2 != nil {{return}} + var _value {type.ValueType.Apply(GoTypeNameVisitor.Ins)} + if _value, err2 = {type.ValueType.Apply(this, "_buf2")}; err2 != nil {{return}} + _v2[_key] = _value + }} + return + }}({bufName})"; + } + + public string Accept(TVector2 type, string bufName) + { + return $"{bufName}.ReadVector2()"; + } + + public string Accept(TVector3 type, string bufName) + { + return $"{bufName}.ReadVector3()"; + } + + public string Accept(TVector4 type, string bufName) + { + return $"{bufName}.ReadVector4()"; + } + + public string Accept(TDateTime type, string bufName) + { + return $"{bufName}.ReadInt()"; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/GoTypeNameVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/GoTypeNameVisitor.cs new file mode 100644 index 0000000..1e50974 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/GoTypeNameVisitor.cs @@ -0,0 +1,125 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class GoTypeNameVisitor : ITypeFuncVisitor<string> + { + public static GoTypeNameVisitor Ins { get; } = new GoTypeNameVisitor(); + + public string Accept(TBool type) + { + return "bool"; + } + + public string Accept(TByte type) + { + return "byte"; + } + + public string Accept(TShort type) + { + return "int16"; + } + + public string Accept(TFshort type) + { + return "int16"; + } + + public string Accept(TInt type) + { + return "int32"; + } + + public string Accept(TFint type) + { + return "int32"; + } + + public string Accept(TLong type) + { + return "int64"; + } + + public string Accept(TFlong type) + { + return "int64"; + } + + public string Accept(TFloat type) + { + return "float32"; + } + + public string Accept(TDouble type) + { + return "float64"; + } + + public string Accept(TEnum type) + { + return "int32"; + } + + public string Accept(TString type) + { + return "string"; + } + + public string Accept(TBytes type) + { + return "[]byte"; + } + + public string Accept(TText type) + { + return "string"; + } + + public string Accept(TBean type) + { + return type.Bean.IsAbstractType ? $"interface{{}}" : $"*{type.Bean.GoFullName}"; + } + + public string Accept(TArray type) + { + return $"[]{type.ElementType.Apply(this)}"; + } + + public string Accept(TList type) + { + return $"[]{type.ElementType.Apply(this)}"; + } + + public string Accept(TSet type) + { + return $"[]{type.ElementType.Apply(this)}"; + } + + public string Accept(TMap type) + { + return $"map[{type.KeyType.Apply(this)}]{type.ValueType.Apply(this)}"; + } + + public string Accept(TVector2 type) + { + return $"math.Vector2"; + } + + public string Accept(TVector3 type) + { + return $"math.Vector3"; + } + + public string Accept(TVector4 type) + { + return $"math.Vector4"; + } + + public string Accept(TDateTime type) + { + return "int32"; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/IsMultiData.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/IsMultiData.cs new file mode 100644 index 0000000..f69c65a --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/IsMultiData.cs @@ -0,0 +1,56 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class IsMultiData : AllFalseVisitor + { + public static IsMultiData Ins { get; } = new IsMultiData(); + + public override bool Accept(TBytes type) + { + return true; + } + + + public override bool Accept(TBean type) + { + return true; + } + + public override bool Accept(TArray type) + { + return true; + } + + public override bool Accept(TList type) + { + return true; + } + + public override bool Accept(TSet type) + { + return true; + } + + public override bool Accept(TMap type) + { + return true; + } + + public override bool Accept(TVector2 type) + { + return true; + } + + public override bool Accept(TVector3 type) + { + return true; + } + + public override bool Accept(TVector4 type) + { + return true; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/IsNotSepTypeVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/IsNotSepTypeVisitor.cs new file mode 100644 index 0000000..9c17dcd --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/IsNotSepTypeVisitor.cs @@ -0,0 +1,66 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class IsNotSepTypeVisitor : AllTrueVisitor + { + public static IsNotSepTypeVisitor Ins { get; } = new IsNotSepTypeVisitor(); + + + public override bool Accept(TString type) + { + return false; + } + + public override bool Accept(TBytes type) + { + return false; + } + + public override bool Accept(TVector2 type) + { + return false; + } + + public override bool Accept(TVector3 type) + { + return false; + } + + public override bool Accept(TVector4 type) + { + return false; + } + + public override bool Accept(TDateTime type) + { + return false; + } + + public override bool Accept(TBean type) + { + return false; + } + + public override bool Accept(TArray type) + { + return false; + } + + public override bool Accept(TList type) + { + return false; + } + + public override bool Accept(TSet type) + { + return false; + } + + public override bool Accept(TMap type) + { + return false; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/JavaDeserializeVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/JavaDeserializeVisitor.cs new file mode 100644 index 0000000..60ebf28 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/JavaDeserializeVisitor.cs @@ -0,0 +1,27 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class JavaDeserializeVisitor : DecoratorFuncVisitor<string, string, string> + { + public static JavaDeserializeVisitor Ins { get; } = new JavaDeserializeVisitor(); + + public override string DoAccept(TType type, string bufName, string fieldName) + { + if (type.IsNullable) + { + return $"if({bufName}.readBool()){{ {type.Apply(JavaUnderingDeserializeVisitor.Ins, bufName, fieldName)} }} else {{ {fieldName} = null; }}"; + } + else + { + return type.Apply(JavaUnderingDeserializeVisitor.Ins, bufName, fieldName); + } + } + + public override string Accept(TBean type, string bufName, string fieldName) + { + return type.Apply(JavaUnderingDeserializeVisitor.Ins, bufName, fieldName); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/JavaRecursiveResolveVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/JavaRecursiveResolveVisitor.cs new file mode 100644 index 0000000..dca80a8 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/JavaRecursiveResolveVisitor.cs @@ -0,0 +1,126 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class JavaRecursiveResolveVisitor : ITypeFuncVisitor<string, string, string> + { + public static JavaRecursiveResolveVisitor Ins { get; } = new JavaRecursiveResolveVisitor(); + + public string Accept(TBool type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TByte type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TShort type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TFshort type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TInt type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TFint type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TLong type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TFlong type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TFloat type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TDouble type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TEnum type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TString type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TBytes type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TText type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TBean type, string fieldName, string tablesName) + { + return $"if ({fieldName} != null) {{{fieldName}.resolve({tablesName});}}"; + } + + public string Accept(TArray type, string fieldName, string tablesName) + { + return $"for({type.ElementType.Apply(JavaDefineTypeName.Ins)} _e : {fieldName}) {{ if (_e != null) _e.resolve({tablesName}); }}"; + } + + public string Accept(TList type, string fieldName, string tablesName) + { + return $"for({type.ElementType.Apply(JavaBoxDefineTypeName.Ins)} _e : {fieldName}) {{ if (_e != null) _e.resolve({tablesName}); }}"; + } + + public string Accept(TSet type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TMap type, string fieldName, string tablesName) + { + return $"for({type.ValueType.Apply(JavaBoxDefineTypeName.Ins)} _e : {fieldName}.values()) {{ if (_e != null) _e.resolve({tablesName}); }}"; + } + + public string Accept(TVector2 type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TVector3 type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TVector4 type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TDateTime type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/JavaUnderingDeserializeVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/JavaUnderingDeserializeVisitor.cs new file mode 100644 index 0000000..cc8ddd4 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/JavaUnderingDeserializeVisitor.cs @@ -0,0 +1,126 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class JavaUnderingDeserializeVisitor : ITypeFuncVisitor<string, string, string> + { + public static JavaUnderingDeserializeVisitor Ins { get; } = new JavaUnderingDeserializeVisitor(); + + public string Accept(TBool type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.readBool();"; + } + + public string Accept(TByte type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.readByte();"; + } + + public string Accept(TShort type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.readShort();"; + } + + public string Accept(TFshort type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.readFshort();"; + } + + public string Accept(TInt type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.readInt();"; + } + + public string Accept(TFint type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.readFint();"; + } + + public string Accept(TLong type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.readLong();"; + } + + public string Accept(TFlong type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.readFlong();"; + } + + public string Accept(TFloat type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.readFloat();"; + } + + public string Accept(TDouble type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.readDouble();"; + } + + public string Accept(TEnum type, string bufName, string fieldName) + { + return $"{fieldName} = {type.DefineEnum.FullNameWithTopModule}.valueOf({bufName}.readInt());"; + } + + public string Accept(TString type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.readString();"; + } + + public string Accept(TBytes type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.readBytes();"; + } + + public string Accept(TText type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.readString();"; + } + + public string Accept(TBean type, string bufName, string fieldName) + { + return $"{fieldName} = {type.Bean.FullNameWithTopModule}.deserialize{type.Bean.Name}({bufName});"; + } + + public string Accept(TArray type, string bufName, string fieldName) + { + return $"{{int n = Math.min({bufName}.readSize(), {bufName}.size());{fieldName} = new {type.ElementType.Apply(JavaDefineTypeName.Ins)}[n];for(var i = 0 ; i < n ; i++) {{ {type.ElementType.Apply(JavaDefineTypeName.Ins)} _e;{type.ElementType.Apply(this, bufName, "_e")} {fieldName}[i] = _e;}}}}"; + } + + public string Accept(TList type, string bufName, string fieldName) + { + return $"{{int n = Math.min({bufName}.readSize(), {bufName}.size());{fieldName} = new {type.Apply(JavaDefineTypeName.Ins)}(n);for(var i = 0 ; i < n ; i++) {{ {type.ElementType.Apply(JavaBoxDefineTypeName.Ins)} _e; {type.ElementType.Apply(this, bufName, "_e")} {fieldName}.add(_e);}}}}"; + } + + public string Accept(TSet type, string bufName, string fieldName) + { + return $"{{int n = Math.min({bufName}.readSize(), {bufName}.size());{fieldName} = new {type.Apply(JavaDefineTypeName.Ins)}(n * 3 / 2);for(var i = 0 ; i < n ; i++) {{ {type.ElementType.Apply(JavaBoxDefineTypeName.Ins)} _e; {type.ElementType.Apply(this, bufName, "_e")} {fieldName}.add(_e);}}}}"; + } + + public string Accept(TMap type, string bufName, string fieldName) + { + return $"{{int n = Math.min({bufName}.readSize(), {bufName}.size());{fieldName} = new {type.Apply(JavaDefineTypeName.Ins)}(n * 3 / 2);for(var i = 0 ; i < n ; i++) {{ {type.KeyType.Apply(JavaBoxDefineTypeName.Ins)} _k; {type.KeyType.Apply(this, bufName, "_k")} {type.ValueType.Apply(JavaBoxDefineTypeName.Ins)} _v; {type.ValueType.Apply(this, bufName, "_v")} {fieldName}.put(_k, _v);}}}}"; + + } + + public string Accept(TVector2 type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.readVector2();"; + } + + public string Accept(TVector3 type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.readVector3();"; + } + + public string Accept(TVector4 type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.readVector4();"; + } + + public string Accept(TDateTime type, string bufName, string fieldName) + { + return $"{fieldName} = {bufName}.readInt();"; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/JsonDataCreator.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/JsonDataCreator.cs new file mode 100644 index 0000000..eb17946 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/JsonDataCreator.cs @@ -0,0 +1,198 @@ +using Luban.Common.Utils; +using Luban.Job.Cfg.Datas; +using Luban.Job.Cfg.Defs; +using Luban.Job.Cfg.Utils; +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System; +using System.Collections.Generic; +using System.Text.Json; + +namespace Luban.Job.Cfg.TypeVisitors +{ + public class JsonDataCreator : ITypeFuncVisitor<JsonElement, DefAssembly, DType> + { + public static JsonDataCreator Ins { get; } = new JsonDataCreator(); + + public DType Accept(TBool type, JsonElement x, DefAssembly ass) + { + return new DBool(x.GetBoolean()); + } + + public DType Accept(TByte type, JsonElement x, DefAssembly ass) + { + return new DByte(x.GetByte()); + } + + public DType Accept(TShort type, JsonElement x, DefAssembly ass) + { + return new DShort(x.GetInt16()); + } + + public DType Accept(TFshort type, JsonElement x, DefAssembly ass) + { + return new DFshort(x.GetInt16()); + } + + public DType Accept(TInt type, JsonElement x, DefAssembly ass) + { + return new DInt(x.GetInt32()); + } + + public DType Accept(TFint type, JsonElement x, DefAssembly ass) + { + return new DFint(x.GetInt32()); + } + + public DType Accept(TLong type, JsonElement x, DefAssembly ass) + { + return new DLong(x.GetInt64()); + } + + public DType Accept(TFlong type, JsonElement x, DefAssembly ass) + { + return new DFlong(x.GetInt64()); + } + + public DType Accept(TFloat type, JsonElement x, DefAssembly ass) + { + return new DFloat(x.GetSingle()); + } + + public DType Accept(TDouble type, JsonElement x, DefAssembly ass) + { + return new DDouble(x.GetDouble()); + } + + public DType Accept(TEnum type, JsonElement x, DefAssembly ass) + { + return new DEnum(type, x.GetString()); + } + + public DType Accept(TString type, JsonElement x, DefAssembly ass) + { + return new DString(x.GetString()); + } + + public DType Accept(TBytes type, JsonElement x, DefAssembly ass) + { + throw new NotImplementedException(); + } + + public DType Accept(TText type, JsonElement x, DefAssembly ass) + { + return new DText(x.GetString()); + } + + public DType Accept(TBean type, JsonElement x, DefAssembly ass) + { + var bean = (DefBean)type.Bean; + + DefBean implBean; + if (bean.IsAbstractType) + { + if (!x.TryGetProperty(DefBean.TYPE_NAME_KEY, out var typeNameProp)) + { + throw new Exception($"结构:{bean.FullName} 是多态类型,必须用 {DefBean.TYPE_NAME_KEY} 字段指定 子类名"); + } + string subType = typeNameProp.GetString(); + var fullName = TypeUtil.MakeFullName(bean.Namespace, subType); + var defType = (DefBean)bean.GetNotAbstractChildType(subType); + //if (defType.IsAbstractType) + //{ + // throw new Exception($"type:{fullName} 是抽象类. 不能创建实例"); + //} + implBean = defType ?? throw new Exception($"type:{fullName} 不是合法类型"); + } + else + { + implBean = bean; + } + + var fields = new List<DType>(); + foreach (var field in implBean.HierarchyFields) + { + if (x.TryGetProperty(field.Name, out var ele)) + { + try + { + fields.Add(field.CType.Apply(this, ele, ass)); + } + catch (Exception e) + { + throw new Exception($"结构:{implBean.FullName} 字段:{field.Name} 读取失败 => {e.Message}", e); + } + } + else + { + throw new Exception($"结构:{implBean.FullName} 字段:{field.Name} 缺失"); + } + } + return new DBean(bean, implBean, fields); + } + + private List<DType> ReadList(TType type, JsonElement e, DefAssembly ass) + { + var list = new List<DType>(); + foreach (var c in e.EnumerateArray()) + { + list.Add(type.Apply(this, c, ass)); + } + return list; + } + + public DType Accept(TArray type, JsonElement x, DefAssembly ass) + { + return new DArray(type, ReadList(type.ElementType, x, ass)); + } + + public DType Accept(TList type, JsonElement x, DefAssembly ass) + { + return new DList(type, ReadList(type.ElementType, x, ass)); + } + + public DType Accept(TSet type, JsonElement x, DefAssembly ass) + { + return new DSet(type, ReadList(type.ElementType, x, ass)); + } + + public DType Accept(TMap type, JsonElement x, DefAssembly ass) + { + var map = new Dictionary<DType, DType>(); + foreach (var e in x.EnumerateArray()) + { + if (e.GetArrayLength() != 2) + { + throw new ArgumentException($"json map 类型的 成员数据项:{e} 必须是 [key,value] 形式的列表"); + } + DType key = type.KeyType.Apply(this, e[0], ass); + DType value = type.ValueType.Apply(this, e[1], ass); + if (!map.TryAdd(key, value)) + { + throw new Exception($"map 的 key:{key} 重复"); + } + } + return new DMap(type, map); + } + + public DType Accept(TDateTime type, JsonElement x, DefAssembly ass) + { + return DataUtil.CreateDateTime(x.GetString(), ass.TimeZone); + } + + public DType Accept(TVector2 type, JsonElement x, DefAssembly ass) + { + return new DVector2(new System.Numerics.Vector2(x.GetProperty("x").GetSingle(), x.GetProperty("y").GetSingle())); + } + + public DType Accept(TVector3 type, JsonElement x, DefAssembly ass) + { + return new DVector3(new System.Numerics.Vector3(x.GetProperty("x").GetSingle(), x.GetProperty("y").GetSingle(), x.GetProperty("z").GetSingle())); + } + + public DType Accept(TVector4 type, JsonElement x, DefAssembly ass) + { + return new DVector4(new System.Numerics.Vector4(x.GetProperty("x").GetSingle(), x.GetProperty("y").GetSingle(), x.GetProperty("z").GetSingle(), x.GetProperty("w").GetSingle())); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/LuaDataCreator.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/LuaDataCreator.cs new file mode 100644 index 0000000..53235c2 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/LuaDataCreator.cs @@ -0,0 +1,240 @@ +using Luban.Common.Utils; +using Luban.Job.Cfg.Datas; +using Luban.Job.Cfg.Defs; +using Luban.Job.Cfg.Utils; +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using Neo.IronLua; +using System; +using System.Collections.Generic; +using System.Numerics; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class LuaDataCreator : ITypeFuncVisitor<object, DefAssembly, DType> + { + public static LuaDataCreator Ins { get; } = new LuaDataCreator(); + + public DType Accept(TBool type, object x, DefAssembly ass) + { + return new DBool((bool)x); + } + + public DType Accept(TByte type, object x, DefAssembly ass) + { + return new DByte((byte)(int)x); + } + + public DType Accept(TShort type, object x, DefAssembly ass) + { + return new DShort((short)(int)x); + } + + public DType Accept(TFshort type, object x, DefAssembly ass) + { + return new DFshort((short)(int)x); + } + + public DType Accept(TInt type, object x, DefAssembly ass) + { + return new DInt((int)x); + } + + public DType Accept(TFint type, object x, DefAssembly ass) + { + return new DFint((int)x); + } + + private long ToLong(object x) + { + return x switch + { + int a => a, + long b => b, + double c => (long)c, + float d => (long)d, + _ => throw new Exception($"{x} 不是 long 类型数据"), + }; + } + + private float ToFloat(object x) + { + return x switch + { + int a => a, + long b => b, + double c => (float)c, + float d => d, + _ => throw new Exception($"{x} 不是 float 类型数据"), + }; + } + + private double ToDouble(object x) + { + return x switch + { + int a => a, + long b => b, + double c => c, + float d => d, + _ => throw new Exception($"{x} 不是 double 类型数据"), + }; + } + + public DType Accept(TLong type, object x, DefAssembly ass) + { + return new DLong(ToLong(x)); + } + + public DType Accept(TFlong type, object x, DefAssembly ass) + { + return new DFlong(ToLong(x)); + } + + public DType Accept(TFloat type, object x, DefAssembly ass) + { + return new DFloat(ToFloat(x)); + } + + public DType Accept(TDouble type, object x, DefAssembly ass) + { + return new DDouble(ToDouble(x)); + } + + public DType Accept(TEnum type, object x, DefAssembly ass) + { + return new DEnum(type, x?.ToString()); + } + + public DType Accept(TString type, object x, DefAssembly ass) + { + return new DString(x?.ToString()); + } + + public DType Accept(TBytes type, object x, DefAssembly ass) + { + throw new NotImplementedException(); + } + + public DType Accept(TText type, object x, DefAssembly ass) + { + return new DText(x?.ToString()); + } + + public DType Accept(TBean type, object x, DefAssembly ass) + { + var table = (LuaTable)x; + var bean = (DefBean)type.Bean; + + DefBean implBean; + if (bean.IsAbstractType) + { + if (!table.ContainsKey(DefBean.TYPE_NAME_KEY)) + { + throw new Exception($"结构:{bean.FullName} 是多态类型,必须用 {DefBean.TYPE_NAME_KEY} 字段指定 子类名"); + } + var subType = (string)table[DefBean.TYPE_NAME_KEY]; + + string fullName = TypeUtil.MakeFullName(bean.Namespace, subType); + var defType = (DefBean)bean.GetNotAbstractChildType(subType); + //if (defType.IsAbstractType) + //{ + // throw new Exception($"type:{fullName} 是抽象类. 不能创建实例"); + //} + implBean = defType ?? throw new Exception($"type:{fullName} 不是合法类型"); + } + else + { + implBean = bean; + } + + var fields = new List<DType>(); + foreach (var field in implBean.HierarchyFields) + { + var ele = table[field.Name]; + + if (ele != null) + { + try + { + // Console.WriteLine("field:{0} type:{1} value:{2}", field.Name, ele.GetType(), ele); + fields.Add(field.CType.Apply(this, ele, ass)); + } + catch (Exception e) + { + throw new Exception($"结构:{implBean.FullName} 字段:{field.Name} 读取失败 => {e.Message}", e); + } + } + else + { + throw new Exception($"结构:{implBean.FullName} 字段:{field.Name} 缺失"); + } + } + return new DBean(bean, implBean, fields); + } + + private List<DType> ReadList(TType type, LuaTable e, DefAssembly ass) + { + var list = new List<DType>(); + foreach (var c in e.ArrayList) + { + list.Add(type.Apply(this, c, ass)); + } + return list; + } + + public DType Accept(TArray type, object x, DefAssembly ass) + { + return new DArray(type, ReadList(type.ElementType, (LuaTable)x, ass)); + } + + public DType Accept(TList type, object x, DefAssembly ass) + { + return new DList(type, ReadList(type.ElementType, (LuaTable)x, ass)); + } + + public DType Accept(TSet type, object x, DefAssembly ass) + { + return new DSet(type, ReadList(type.ElementType, (LuaTable)x, ass)); + } + + public DType Accept(TMap type, object x, DefAssembly ass) + { + var table = (LuaTable)x; + var map = new Dictionary<DType, DType>(); + foreach (var e in table.Values) + { + DType key = type.KeyType.Apply(this, e.Key, ass); + DType value = type.ValueType.Apply(this, e.Value, ass); + if (!map.TryAdd(key, value)) + { + throw new Exception($"map 的 key:{key} 重复"); + } + } + return new DMap(type, map); + } + + public DType Accept(TVector2 type, object x, DefAssembly ass) + { + var table = (LuaTable)x; + return new DVector2(new Vector2(ToFloat(table["x"]), ToFloat(table["y"]))); + } + + public DType Accept(TVector3 type, object x, DefAssembly ass) + { + var table = (LuaTable)x; + return new DVector3(new Vector3(ToFloat(table["x"]), ToFloat(table["y"]), ToFloat(table["z"]))); + } + + public DType Accept(TVector4 type, object x, DefAssembly ass) + { + var table = (LuaTable)x; + return new DVector4(new Vector4(ToFloat(table["x"]), ToFloat(table["y"]), ToFloat(table["z"]), ToFloat(table["w"]))); + } + + public DType Accept(TDateTime type, object x, DefAssembly ass) + { + return DataUtil.CreateDateTime(x.ToString(), ass.TimeZone); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/MultiRowExcelDataCreator.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/MultiRowExcelDataCreator.cs new file mode 100644 index 0000000..12611b2 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/MultiRowExcelDataCreator.cs @@ -0,0 +1,161 @@ +using Luban.Job.Cfg.Datas; +using Luban.Job.Cfg.DataSources.Excel; +using Luban.Job.Cfg.Defs; +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System; +using System.Collections.Generic; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class MultiRowExcelDataCreator : ITypeFuncVisitor<IEnumerable<ExcelStream>, bool, DefAssembly, DType> + { + public static MultiRowExcelDataCreator Ins { get; } = new MultiRowExcelDataCreator(); + + public DType Accept(TBool type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass) + { + throw new NotImplementedException(); + } + + public DType Accept(TByte type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass) + { + throw new NotImplementedException(); + } + + public DType Accept(TShort type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass) + { + throw new NotImplementedException(); + } + + public DType Accept(TFshort type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass) + { + throw new NotImplementedException(); + } + + public DType Accept(TInt type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass) + { + throw new NotImplementedException(); + } + + public DType Accept(TFint type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass) + { + throw new NotImplementedException(); + } + + public DType Accept(TLong type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass) + { + throw new NotImplementedException(); + } + + public DType Accept(TFlong type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass) + { + throw new NotImplementedException(); + } + + public DType Accept(TFloat type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass) + { + throw new NotImplementedException(); + } + + public DType Accept(TDouble type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass) + { + throw new NotImplementedException(); + } + + public DType Accept(TEnum type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass) + { + throw new NotImplementedException(); + } + + public DType Accept(TString type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass) + { + throw new NotImplementedException(); + } + + public DType Accept(TBytes type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass) + { + throw new NotImplementedException(); + } + + public DType Accept(TText type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass) + { + throw new NotImplementedException(); + } + + public DType Accept(TBean type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass) + { + throw new NotImplementedException(); + } + + private List<DType> ReadMultiRow(TType type, IEnumerable<ExcelStream> rows, DefAssembly ass) + { + var list = new List<DType>(); + foreach (var stream in rows) + { + try + { + list.Add(type.Apply(ExcelDataCreator.Ins, null, stream, ass)); + } + catch (Exception e) + { + throw new Exception($"位置:{stream.CurrentExcelPosition} 出错 ==> {e.Message}", e); + } + } + return list; + } + + public DType Accept(TArray type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass) + { + return new DArray(type, ReadMultiRow(type.ElementType, x, ass)); + } + + public DType Accept(TList type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass) + { + return new DList(type, ReadMultiRow(type.ElementType, x, ass)); + } + + public DType Accept(TSet type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass) + { + return new DSet(type, ReadMultiRow(type.ElementType, x, ass)); + } + + public DType Accept(TMap type, IEnumerable<ExcelStream> rows, bool y, DefAssembly ass) + { + var map = new Dictionary<DType, DType>(); + foreach (var stream in rows) + { + try + { + DType key = type.KeyType.Apply(ExcelDataCreator.Ins, null, stream, ass); + DType value = type.ValueType.Apply(ExcelDataCreator.Ins, null, stream, ass); + map.Add(key, value); + } + catch (Exception e) + { + throw new Exception($"位置:{stream.CurrentExcelPosition} 出错 ==> {e.Message}", e); + } + } + return new DMap(type, map); + } + + public DType Accept(TVector2 type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass) + { + throw new NotImplementedException(); + } + + public DType Accept(TVector3 type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass) + { + throw new NotImplementedException(); + } + + public DType Accept(TVector4 type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass) + { + throw new NotImplementedException(); + } + + public DType Accept(TDateTime type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/PyDeserializeVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/PyDeserializeVisitor.cs new file mode 100644 index 0000000..6197c4d --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/PyDeserializeVisitor.cs @@ -0,0 +1,41 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class PyDeserializeVisitor : DecoratorFuncVisitor<string, string, string> + { + public static PyDeserializeVisitor Py3Ins { get; } = new PyDeserializeVisitor(true); + + + public static PyDeserializeVisitor Py27Ins { get; } = new PyDeserializeVisitor(false); + + public PyDeserializeVisitor(bool py3) + { + Python3 = py3; + + UnderringVisitor = py3 ? PyUnderingDeserializeVisitor.Py3Ins : PyUnderingDeserializeVisitor.Py27Ins; + } + + public bool Python3 { get; } + + PyUnderingDeserializeVisitor UnderringVisitor { get; } + + public override string DoAccept(TType type, string jsonFieldName, string fieldName) + { + if (type.IsNullable) + { + return $"if {jsonFieldName} != None: {type.Apply(UnderringVisitor, jsonFieldName, fieldName)}"; + } + else + { + return type.Apply(UnderringVisitor, jsonFieldName, fieldName); + } + } + + public override string Accept(TBean type, string bufName, string fieldName) + { + return type.Apply(UnderringVisitor, bufName, fieldName); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/PyUnderingDeserializeVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/PyUnderingDeserializeVisitor.cs new file mode 100644 index 0000000..799988d --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/PyUnderingDeserializeVisitor.cs @@ -0,0 +1,162 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class PyUnderingDeserializeVisitor : ITypeFuncVisitor<string, string, string> + { + public static PyUnderingDeserializeVisitor Py3Ins { get; } = new PyUnderingDeserializeVisitor(true); + + public static PyUnderingDeserializeVisitor Py27Ins { get; } = new PyUnderingDeserializeVisitor(false); + + public PyUnderingDeserializeVisitor(bool py3) + { + Python3 = py3; + } + + public bool Python3 { get; } + + public string Accept(TBool type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName}"; + } + + public string Accept(TByte type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName}"; + } + + public string Accept(TShort type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName}"; + } + + public string Accept(TFshort type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName}"; + } + + public string Accept(TInt type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName}"; + } + + public string Accept(TFint type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName}"; + } + + public string Accept(TLong type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName}"; + } + + public string Accept(TFlong type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName}"; + } + + public string Accept(TFloat type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName}"; + } + + public string Accept(TDouble type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName}"; + } + + public string Accept(TEnum type, string jsonVarName, string fieldName) + { + return Python3 ? $"{fieldName} = {type.DefineEnum.PyFullName}({jsonVarName})" : $"{fieldName} = {jsonVarName}"; + } + + public string Accept(TString type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName}"; + } + + public string Accept(TBytes type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName}"; + } + + public string Accept(TText type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName}"; + } + + public string Accept(TBean type, string jsonVarName, string fieldName) + { + if (type.Bean.IsAbstractType) + { + return $"{fieldName} = {type.Bean.PyFullName}.fromJson({jsonVarName})"; + } + else + { + return $"{fieldName} = {type.Bean.PyFullName}({jsonVarName})"; + } + } + + public string Accept(TArray type, string jsonVarName, string fieldName) + { + if (type.Apply(SimpleJsonTypeVisitor.Ins)) + { + return $"{fieldName} = {jsonVarName}"; + } + else + { + return $"{fieldName} = []\n for _ele in {jsonVarName}: {type.ElementType.Apply(this, "_ele", "_e")}; {fieldName}.append(_e)"; + } + } + + public string Accept(TList type, string jsonVarName, string fieldName) + { + if (type.Apply(SimpleJsonTypeVisitor.Ins)) + { + return $"{fieldName} = {jsonVarName}"; + } + else + { + return $"{fieldName} = []\n for _ele in {jsonVarName}: {type.ElementType.Apply(this, "_ele", "_e")}; {fieldName}.append(_e)"; + } + } + + public string Accept(TSet type, string jsonVarName, string fieldName) + { + if (type.Apply(SimpleJsonTypeVisitor.Ins)) + { + return $"{fieldName} = {jsonVarName}"; + } + else + { + return $"{fieldName} = set()\n for _ele in {jsonVarName}: {type.ElementType.Apply(this, "_ele", "_e")}; {fieldName}.add(_e)"; + } + } + + public string Accept(TMap type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {{}}\n for _ek, _ev in {jsonVarName}: {type.KeyType.Apply(this, "_ek", "_k")}; {type.ValueType.Apply(this, "_ev", "_v")}; {fieldName}[_k] =_v"; + } + + public string Accept(TVector2 type, string jsonVarName, string fieldName) + { + return $"{fieldName} = Vector2.fromJson({jsonVarName})"; + } + + public string Accept(TVector3 type, string jsonVarName, string fieldName) + { + return $"{fieldName} = Vector3.fromJson({jsonVarName})"; + } + + public string Accept(TVector4 type, string jsonVarName, string fieldName) + { + return $"{fieldName} = Vector4.fromJson({jsonVarName})"; + } + + public string Accept(TDateTime type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName}"; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/RefTypeVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/RefTypeVisitor.cs new file mode 100644 index 0000000..54f3c94 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/RefTypeVisitor.cs @@ -0,0 +1,149 @@ +using Luban.Job.Cfg.Defs; +using Luban.Job.Common.Defs; +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System.Collections.Generic; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class RefTypeVisitor : ITypeActionVisitor<Dictionary<string, DefTypeBase>> + { + public static RefTypeVisitor Ins { get; } = new RefTypeVisitor(); + + public void Accept(TBool type, Dictionary<string, DefTypeBase> x) + { + + } + + public void Accept(TByte type, Dictionary<string, DefTypeBase> x) + { + + } + + public void Accept(TShort type, Dictionary<string, DefTypeBase> x) + { + + } + + public void Accept(TFshort type, Dictionary<string, DefTypeBase> x) + { + + } + + public void Accept(TInt type, Dictionary<string, DefTypeBase> x) + { + + } + + public void Accept(TFint type, Dictionary<string, DefTypeBase> x) + { + + } + + public void Accept(TLong type, Dictionary<string, DefTypeBase> x) + { + + } + + public void Accept(TFlong type, Dictionary<string, DefTypeBase> x) + { + + } + + public void Accept(TFloat type, Dictionary<string, DefTypeBase> x) + { + + } + + public void Accept(TDouble type, Dictionary<string, DefTypeBase> x) + { + + } + + public void Accept(TEnum type, Dictionary<string, DefTypeBase> x) + { + x.TryAdd(type.DefineEnum.FullName, type.DefineEnum); + } + + public void Accept(TString type, Dictionary<string, DefTypeBase> x) + { + + } + + public void Accept(TBytes type, Dictionary<string, DefTypeBase> x) + { + + } + + public void Accept(TText type, Dictionary<string, DefTypeBase> x) + { + + } + + + void Walk(DefBean type, Dictionary<string, DefTypeBase> types) + { + if (types.TryAdd(type.FullName, type)) + { + foreach (var f in type.Fields) + { + f.CType.Apply(this, types); + } + if (type.Children != null) + { + foreach (DefBean c in type.Children) + { + Walk(c, types); + } + } + } + } + + public void Accept(TBean type, Dictionary<string, DefTypeBase> x) + { + var root = (DefBean)type.Bean.RootDefType; + Walk(root, x); + } + + public void Accept(TArray type, Dictionary<string, DefTypeBase> x) + { + type.ElementType.Apply(this, x); + } + + public void Accept(TList type, Dictionary<string, DefTypeBase> x) + { + type.ElementType.Apply(this, x); + } + + public void Accept(TSet type, Dictionary<string, DefTypeBase> x) + { + type.ElementType.Apply(this, x); + } + + public void Accept(TMap type, Dictionary<string, DefTypeBase> x) + { + type.KeyType.Apply(this, x); + type.ValueType.Apply(this, x); + } + + public void Accept(TVector2 type, Dictionary<string, DefTypeBase> x) + { + + } + + public void Accept(TVector3 type, Dictionary<string, DefTypeBase> x) + { + + } + + public void Accept(TVector4 type, Dictionary<string, DefTypeBase> x) + { + + } + + public void Accept(TDateTime type, Dictionary<string, DefTypeBase> x) + { + + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/SimpleJsonTypeVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/SimpleJsonTypeVisitor.cs new file mode 100644 index 0000000..68ff85d --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/SimpleJsonTypeVisitor.cs @@ -0,0 +1,56 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class SimpleJsonTypeVisitor : AllTrueVisitor + { + public static SimpleJsonTypeVisitor Ins { get; } = new SimpleJsonTypeVisitor(); + + public override bool Accept(TEnum type) + { + return false; + } + + public override bool Accept(TVector2 type) + { + return false; + } + + public override bool Accept(TVector3 type) + { + return false; + } + + public override bool Accept(TVector4 type) + { + return false; + } + + public override bool Accept(TBean type) + { + //return type.Bean.IsNotAbstractType && type.Bean.HierarchyFields.All(f => f.CType.Apply(this)); + return false; + } + + public override bool Accept(TArray type) + { + return type.ElementType.Apply(this); + } + + public override bool Accept(TList type) + { + return type.ElementType.Apply(this); + } + + public override bool Accept(TSet type) + { + return type.ElementType.Apply(this); + } + + public override bool Accept(TMap type) + { + return false; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/StringDataCreator.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/StringDataCreator.cs new file mode 100644 index 0000000..d4bb248 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/StringDataCreator.cs @@ -0,0 +1,197 @@ +using Luban.Job.Cfg.Datas; +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class StringDataCreator : ITypeFuncVisitor<string, DType> + { + public static StringDataCreator Ins { get; } = new StringDataCreator(); + + public DType Accept(TBool type, string x) + { + if (bool.TryParse(x, out var b)) + { + return new DBool(b); + } + else + { + throw new Exception($"{x} 不是bool类型"); + } + } + + public DType Accept(TByte type, string x) + { + if (byte.TryParse(x, out var b)) + { + return new DByte(b); + } + else + { + throw new Exception($"{x} 不是byte类型"); + } + } + + public DType Accept(TShort type, string x) + { + if (short.TryParse(x, out var b)) + { + return new DShort(b); + } + else + { + throw new Exception($"{x} 不是short类型"); + } + } + + public DType Accept(TFshort type, string x) + { + if (short.TryParse(x, out var b)) + { + return new DFshort(b); + } + else + { + throw new Exception($"{x} 不是short类型"); + } + } + + public DType Accept(TInt type, string x) + { + if (int.TryParse(x, out var b)) + { + return new DInt(b); + } + else + { + throw new Exception($"{x} 不是int类型"); + } + } + + public DType Accept(TFint type, string x) + { + if (int.TryParse(x, out var b)) + { + return new DFint(b); + } + else + { + throw new Exception($"{x} 不是int类型"); + } + } + + public DType Accept(TLong type, string x) + { + if (long.TryParse(x, out var b)) + { + return new DLong(b); + } + else + { + throw new Exception($"{x} 不是long类型"); + } + } + + public DType Accept(TFlong type, string x) + { + if (long.TryParse(x, out var b)) + { + return new DFlong(b); + } + else + { + throw new Exception($"{x} 不是long类型"); + } + } + + public DType Accept(TFloat type, string x) + { + if (float.TryParse(x, out var b)) + { + return new DFloat(b); + } + else + { + throw new Exception($"{x} 不是float类型"); + } + } + + public DType Accept(TDouble type, string x) + { + if (double.TryParse(x, out var b)) + { + return new DDouble(b); + } + else + { + throw new Exception($"{x} 不是double类型"); + } + } + + public DType Accept(TEnum type, string x) + { + return new DEnum(type, x); + } + + public DType Accept(TString type, string x) + { + return new DString(x); + } + + public DType Accept(TBytes type, string x) + { + throw new NotImplementedException(); + } + + public DType Accept(TText type, string x) + { + return new DText(x); + } + + public DType Accept(TBean type, string x) + { + throw new NotImplementedException(); + } + + public DType Accept(TArray type, string x) + { + throw new NotImplementedException(); + } + + public DType Accept(TList type, string x) + { + throw new NotImplementedException(); + } + + public DType Accept(TSet type, string x) + { + throw new NotImplementedException(); + } + + public DType Accept(TMap type, string x) + { + throw new NotImplementedException(); + } + + public DType Accept(TVector2 type, string x) + { + throw new NotImplementedException(); + } + + public DType Accept(TVector3 type, string x) + { + throw new NotImplementedException(); + } + + public DType Accept(TVector4 type, string x) + { + throw new NotImplementedException(); + } + + public DType Accept(TDateTime type, string x) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/TsDeserializeVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/TsDeserializeVisitor.cs new file mode 100644 index 0000000..08f0427 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/TsDeserializeVisitor.cs @@ -0,0 +1,27 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class TsDeserializeVisitor : DecoratorFuncVisitor<string, string, string> + { + public static TsDeserializeVisitor Ins { get; } = new TsDeserializeVisitor(); + + public override string DoAccept(TType type, string jsonFieldName, string fieldName) + { + if (type.IsNullable) + { + return $"if({jsonFieldName} != null) {{ {type.Apply(TsUnderingDeserializeVisitor.Ins, jsonFieldName, fieldName)} }} else {{ {fieldName} = null; }}"; + } + else + { + return type.Apply(TsUnderingDeserializeVisitor.Ins, jsonFieldName, fieldName); + } + } + + public override string Accept(TBean type, string bufName, string fieldName) + { + return type.Apply(TsUnderingDeserializeVisitor.Ins, bufName, fieldName); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/TsRecursiveResolveVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/TsRecursiveResolveVisitor.cs new file mode 100644 index 0000000..c41f0e6 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/TsRecursiveResolveVisitor.cs @@ -0,0 +1,126 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class TsRecursiveResolveVisitor : ITypeFuncVisitor<string, string, string> + { + public static TsRecursiveResolveVisitor Ins { get; } = new TsRecursiveResolveVisitor(); + + public string Accept(TBool type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TByte type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TShort type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TFshort type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TInt type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TFint type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TLong type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TFlong type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TFloat type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TDouble type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TEnum type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TString type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TBytes type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TText type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TBean type, string fieldName, string tablesName) + { + return $"if ({fieldName} != null) {{ {fieldName}.resolve({tablesName});}}"; + } + + public string Accept(TArray type, string fieldName, string tablesName) + { + return $"for(let _e of {fieldName}) {{ if (_e != null) {{ _e.resolve({tablesName}); }} }}"; + } + + public string Accept(TList type, string fieldName, string tablesName) + { + return $"for(let _e of {fieldName}) {{ if (_e != null ) {{_e.resolve({tablesName});}} }}"; + } + + public string Accept(TSet type, string fieldName, string tablesName) + { + throw new NotSupportedException(); + } + + public string Accept(TMap type, string fieldName, string tablesName) + { + return $"for(let _e of {fieldName}.values()) {{ if (_e != null) {{_e.resolve({tablesName});}} }}"; + } + + public string Accept(TVector2 type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TVector3 type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TVector4 type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + + public string Accept(TDateTime type, string fieldName, string tablesName) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/TsUnderingDeserializeVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/TsUnderingDeserializeVisitor.cs new file mode 100644 index 0000000..346b5e3 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/TsUnderingDeserializeVisitor.cs @@ -0,0 +1,154 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class TsUnderingDeserializeVisitor : ITypeFuncVisitor<string, string, string> + { + public static TsUnderingDeserializeVisitor Ins { get; } = new TsUnderingDeserializeVisitor(); + + public string Accept(TBool type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName};"; + } + + public string Accept(TByte type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName};"; + } + + public string Accept(TShort type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName};"; + } + + public string Accept(TFshort type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName};"; + } + + public string Accept(TInt type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName};"; + } + + public string Accept(TFint type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName};"; + } + + public string Accept(TLong type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName};"; + } + + public string Accept(TFlong type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName};"; + } + + public string Accept(TFloat type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName};"; + } + + public string Accept(TDouble type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName};"; + } + + public string Accept(TEnum type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName} as number;"; + } + + public string Accept(TString type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName};"; + } + + public string Accept(TBytes type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName};"; + } + + public string Accept(TText type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName};"; + } + + public string Accept(TBean type, string jsonVarName, string fieldName) + { + if (type.Bean.IsAbstractType) + { + return $"{fieldName} = {type.Bean.FullName}.deserialize({jsonVarName});"; + } + else + { + return $"{fieldName} = new {type.Bean.FullName}({jsonVarName});"; + } + } + + public string Accept(TArray type, string jsonVarName, string fieldName) + { + if (type.Apply(SimpleJsonTypeVisitor.Ins)) + { + return $"{fieldName} = {jsonVarName};"; + } + else + { + return $"{{ {fieldName} = []; for(var _ele of {jsonVarName}) {{ let _e :{type.ElementType.Apply(TsDefineTypeName.Ins)};{type.ElementType.Apply(this, "_ele", "_e")} {fieldName}.push(_e);}}}}"; + } + } + + public string Accept(TList type, string jsonVarName, string fieldName) + { + if (type.Apply(SimpleJsonTypeVisitor.Ins)) + { + return $"{fieldName} = {jsonVarName};"; + } + else + { + return $"{{ {fieldName} = []; for(var _ele of {jsonVarName}) {{ let _e : {type.ElementType.Apply(TsDefineTypeName.Ins)};{type.ElementType.Apply(this, "_ele", "_e")} {fieldName}.push(_e);}}}}"; + } + } + + public string Accept(TSet type, string jsonVarName, string fieldName) + { + if (type.Apply(SimpleJsonTypeVisitor.Ins)) + { + return $"{fieldName} = {jsonVarName};"; + } + else + { + return $"{{ {fieldName} = new {type.Apply(TsDefineTypeName.Ins)}(); for(var _ele of {jsonVarName}) {{ let _e:{type.ElementType.Apply(TsDefineTypeName.Ins)};{type.ElementType.Apply(this, "_ele", "_e")} {fieldName}.add(_e);}}}}"; + } + } + + public string Accept(TMap type, string jsonVarName, string fieldName) + { + return $"{fieldName} = new {type.Apply(TsDefineTypeName.Ins)}(); for(var _entry_ of {jsonVarName}) {{ let _k:{type.KeyType.Apply(TsDefineTypeName.Ins)}; {type.KeyType.Apply(this, "_entry_[0]", "_k")} let _v:{type.ValueType.Apply(TsDefineTypeName.Ins)}; {type.ValueType.Apply(this, "_entry_[1]", "_v")} {fieldName}.set(_k, _v); }}"; + + } + + public string Accept(TVector2 type, string jsonVarName, string fieldName) + { + return $"{fieldName} = Vector2.fromJson({jsonVarName});"; + } + + public string Accept(TVector3 type, string jsonVarName, string fieldName) + { + return $"{fieldName} = Vector3.fromJson({jsonVarName});"; + } + + public string Accept(TVector4 type, string jsonVarName, string fieldName) + { + return $"{fieldName} = Vector4.fromJson({jsonVarName});"; + } + + public string Accept(TDateTime type, string jsonVarName, string fieldName) + { + return $"{fieldName} = {jsonVarName};"; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/UeBpCppDefineTypeVisitor.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/UeBpCppDefineTypeVisitor.cs new file mode 100644 index 0000000..5dd94bb --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/UeBpCppDefineTypeVisitor.cs @@ -0,0 +1,128 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class UeBpCppDefineTypeVisitor : ITypeFuncVisitor<string> + { + public static UeBpCppDefineTypeVisitor Ins { get; } = new UeBpCppDefineTypeVisitor(); + + public string Accept(TBool type) + { + return "bool"; + } + + public string Accept(TByte type) + { + return "uint8"; + } + + public string Accept(TShort type) + { + return "int16"; + } + + public string Accept(TFshort type) + { + return "int16"; + } + + public string Accept(TInt type) + { + return "int32"; + } + + public string Accept(TFint type) + { + return "int32"; + } + + public string Accept(TLong type) + { + return "int64"; + } + + public string Accept(TFlong type) + { + return "int64"; + } + + public string Accept(TFloat type) + { + return "float"; + } + + public string Accept(TDouble type) + { + return "double"; + } + + public virtual string Accept(TEnum type) + { + //return type.DefineEnum.UeBpFullName; + throw new NotImplementedException(); + } + + public string Accept(TString type) + { + return "FString"; + } + + public string Accept(TBytes type) + { + throw new NotImplementedException(); + } + + public string Accept(TText type) + { + return "FString"; + } + + public virtual string Accept(TBean type) + { + //return type.Bean.UeBpFullName + "*"; + throw new NotImplementedException(); + } + + public string Accept(TArray type) + { + return $"TArray<{type.ElementType.Apply(this)}>"; + } + + public string Accept(TList type) + { + return $"TArray<{type.ElementType.Apply(this)}>"; + } + + public string Accept(TSet type) + { + return $"TArray<{type.ElementType.Apply(this)}>"; + } + + public string Accept(TMap type) + { + return $"TMap<{type.KeyType.Apply(this)}, {type.ValueType.Apply(this)}>"; + } + + public string Accept(TVector2 type) + { + return "FVector2D"; + } + + public string Accept(TVector3 type) + { + return "FVector"; + } + + public string Accept(TVector4 type) + { + return "FVector4"; + } + + public string Accept(TDateTime type) + { + return "int32"; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/TypeVisitors/XmlDataCreator.cs b/src/Luban.Job.Cfg/Source/TypeVisitors/XmlDataCreator.cs new file mode 100644 index 0000000..3231e43 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/TypeVisitors/XmlDataCreator.cs @@ -0,0 +1,200 @@ +using Luban.Common.Utils; +using Luban.Job.Cfg.Datas; +using Luban.Job.Cfg.Defs; +using Luban.Job.Cfg.Utils; +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; + +namespace Luban.Job.Cfg.TypeVisitors +{ + class XmlDataCreator : ITypeFuncVisitor<XElement, DefAssembly, DType> + { + public static XmlDataCreator Ins { get; } = new XmlDataCreator(); + + public DType Accept(TBool type, XElement x, DefAssembly ass) + { + return new DBool(bool.Parse(x.Value.Trim().ToLower())); + } + + public DType Accept(TByte type, XElement x, DefAssembly ass) + { + return new DByte(byte.Parse(x.Value.Trim())); + } + + public DType Accept(TShort type, XElement x, DefAssembly ass) + { + return new DShort(short.Parse(x.Value.Trim())); + } + + public DType Accept(TFshort type, XElement x, DefAssembly ass) + { + return new DFshort(short.Parse(x.Value.Trim())); + } + + public DType Accept(TInt type, XElement x, DefAssembly ass) + { + return new DInt(int.Parse(x.Value.Trim())); + } + + public DType Accept(TFint type, XElement x, DefAssembly ass) + { + return new DFint(int.Parse(x.Value.Trim())); + } + + public DType Accept(TLong type, XElement x, DefAssembly ass) + { + return new DLong(long.Parse(x.Value.Trim())); + } + + public DType Accept(TFlong type, XElement x, DefAssembly ass) + { + return new DFlong(long.Parse(x.Value.Trim())); + } + + public DType Accept(TFloat type, XElement x, DefAssembly ass) + { + return new DFloat(float.Parse(x.Value.Trim())); + } + + public DType Accept(TDouble type, XElement x, DefAssembly ass) + { + return new DDouble(double.Parse(x.Value.Trim())); + } + + public DType Accept(TEnum type, XElement x, DefAssembly ass) + { + return new DEnum(type, x.Value.Trim()); + } + + public DType Accept(TString type, XElement x, DefAssembly ass) + { + return new DString(x.Value); + } + + public DType Accept(TBytes type, XElement x, DefAssembly ass) + { + throw new NotImplementedException(); + } + + public DType Accept(TText type, XElement x, DefAssembly ass) + { + return new DText(x.Value); + } + + public DType Accept(TBean type, XElement x, DefAssembly ass) + { + var bean = (DefBean)type.Bean; + + DefBean implBean; + if (bean.IsAbstractType) + { + string subType = x.Attribute(DefBean.TYPE_NAME_KEY)?.Value; + if (string.IsNullOrWhiteSpace(subType)) + { + throw new Exception($"bean:{bean.FullName}是多态,需要指定{DefBean.TYPE_NAME_KEY}属性.\n xml:{x}"); + } + var fullName = TypeUtil.MakeFullName(bean.Namespace, subType); + var defType = (DefBean)bean.GetNotAbstractChildType(subType); + if (defType == null) + { + throw new Exception($"type:{fullName} 不是合法类型"); + } + //if (defType.IsAbstractType) + //{ + // throw new Exception($"type:{fullName} 是抽象类. 不能创建实例"); + //} + implBean = defType; + } + else + { + implBean = bean; + } + + var fields = new List<DType>(); + foreach (var field in implBean.HierarchyFields) + { + var feles = x.Elements(field.Name); + XElement fele = feles.FirstOrDefault(); + if (fele == null) + { + throw new Exception($"字段:{field.Name} 缺失"); + } + + try + { + fields.Add(field.CType.Apply(this, fele, ass)); + } + catch (Exception e) + { + throw new Exception($"结构:{implBean.FullName} 字段:{field.Name} 读取失败 => {e.Message}", e); + } + + } + return new DBean(bean, implBean, fields); + } + + private List<DType> ReadList(TType type, XElement x, DefAssembly ass) + { + var list = new List<DType>(); + foreach (var e in x.Elements()) + { + list.Add(type.Apply(this, e, ass)); + } + return list; + } + + public DType Accept(TArray type, XElement x, DefAssembly ass) + { + return new DArray(type, ReadList(type.ElementType, x, ass)); + } + + public DType Accept(TList type, XElement x, DefAssembly ass) + { + return new DList(type, ReadList(type.ElementType, x, ass)); + } + + public DType Accept(TSet type, XElement x, DefAssembly ass) + { + return new DSet(type, ReadList(type.ElementType, x, ass)); + } + + public DType Accept(TMap type, XElement x, DefAssembly ass) + { + var map = new Dictionary<DType, DType>(); + foreach (var e in x.Elements()) + { + DType key = type.KeyType.Apply(this, e.Element("key"), ass); + DType value = type.ValueType.Apply(this, e.Element("value"), ass); + if (!map.TryAdd(key, value)) + { + throw new Exception($"map 的 key:{key} 重复"); + } + } + return new DMap(type, map); + } + + public DType Accept(TVector2 type, XElement x, DefAssembly ass) + { + return DataUtil.CreateVector(type, x.Value); + } + + public DType Accept(TVector3 type, XElement x, DefAssembly ass) + { + return DataUtil.CreateVector(type, x.Value); + } + + public DType Accept(TVector4 type, XElement x, DefAssembly ass) + { + return DataUtil.CreateVector(type, x.Value); + } + + public DType Accept(TDateTime type, XElement x, DefAssembly ass) + { + return DataUtil.CreateDateTime(x.Value, ass.TimeZone); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Utils/DataUtil.cs b/src/Luban.Job.Cfg/Source/Utils/DataUtil.cs new file mode 100644 index 0000000..2f1834d --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Utils/DataUtil.cs @@ -0,0 +1,74 @@ +using Luban.Job.Cfg.Datas; +using Luban.Job.Common.Types; +using System; +using System.IO; + +namespace Luban.Job.Cfg.Utils +{ + static class DataUtil + { + public static string[] SplitVectorString(string x) + { + return x.Split(',', '_', ';'); + } + + public static string[] SplitStringByAnySepChar(string x, string sep) + { + return x.Split(sep.ToCharArray()); + } + + public static DType CreateVector(TVector2 type, string x) + { + var values = DataUtil.SplitVectorString(x); + + return new DVector2(new System.Numerics.Vector2(float.Parse(values[0]), float.Parse(values[1]))); + + } + + public static DType CreateVector(TVector3 type, string x) + { + var values = DataUtil.SplitVectorString(x); + + return new DVector3(new System.Numerics.Vector3(float.Parse(values[0]), float.Parse(values[1]), float.Parse(values[2]))); + + } + + public static DType CreateVector(TVector4 type, string x) + { + var values = DataUtil.SplitVectorString(x); + return new DVector4(new System.Numerics.Vector4(float.Parse(values[0]), float.Parse(values[1]), float.Parse(values[2]), float.Parse(values[3]))); + } + + public static DDateTime CreateDateTime(string x, TimeZoneInfo timeZoneInfo) + { + + DateTime dateTime = DateTime.ParseExact(x, + new string[] { + "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM-dd HH", "yyyy-MM-dd", + //"yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM/dd HH", "yyyy/MM/dd", + }, + System.Globalization.CultureInfo.InvariantCulture); + return new DDateTime(TimeZoneInfo.ConvertTimeToUtc(dateTime, timeZoneInfo)); + } + + public static byte[] StreamToBytes(Stream stream) + { + byte[] bytes = new byte[stream.Length]; + stream.Seek(0, SeekOrigin.Begin); + stream.Read(bytes, 0, bytes.Length); + return bytes; + } + + public static string GetSourceFile(DType data) + { + return (string)data.Source; + } + + //public static string Data2String(DType data) + //{ + // var s = new StringBuilder(); + // data.Apply(VisitorToString.Ins, s); + // return s.ToString(); + //} + } +} diff --git a/src/Luban.Job.Cfg/Source/ValidatorContext.cs b/src/Luban.Job.Cfg/Source/ValidatorContext.cs new file mode 100644 index 0000000..585027f --- /dev/null +++ b/src/Luban.Job.Cfg/Source/ValidatorContext.cs @@ -0,0 +1,203 @@ +using Luban.Common.Utils; +using Luban.Config.Common.RawDefs; +using Luban.Job.Cfg.Datas; +using Luban.Job.Cfg.DataVisitors; +using Luban.Job.Cfg.Defs; +using Luban.Job.Cfg.Utils; +using Luban.Job.Cfg.Validators; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Luban.Job.Cfg +{ + public class PathQuery + { + public PathValidator Validator { get; set; } + + public string DataPath { get; set; } + + public string Value { get; set; } + + public string Source { get; set; } + + public object QueryPath { get; set; } + } + + public class ValidatorContext + { + [ThreadStatic] + private static ValidatorVisitor t_visitor; + + public static ValidatorVisitor CurrentVisitor { get => t_visitor; set => t_visitor = value; } + + public static string CurrentRecordPath => TypeUtil.MakeFullName(CurrentVisitor.Path); + + public DefAssembly Assembly { get; } + + public string RootDir { get; } + + + + private readonly List<PathQuery> _pathQuerys = new List<PathQuery>(1000); + + public void AddPathQuery(PathQuery query) + { + lock (this) + { + _pathQuerys.Add(query); + } + } + + + public List<PathQuery> GetPathQueries() => _pathQuerys; + + public ValidatorContext(DefAssembly ass, string rootDir) + { + this.Assembly = ass; + this.RootDir = rootDir; + } + + public async Task ValidateTables(IEnumerable<DefTable> tables) + { + var tasks = new List<Task>(); + foreach (var t in tables) + { + tasks.Add(Task.Run(() => + { + var records = t.Assembly.GetTableDataList(t); + ValidateTableModeIndex(t, records); + + var visitor = new ValidatorVisitor(this); + try + { + CurrentVisitor = visitor; + visitor.ValidateTable(t, records); + } + finally + { + CurrentVisitor = null; + } + })); + } + await Task.WhenAll(tasks); + if (!string.IsNullOrWhiteSpace(RootDir)) + { + await ValidatePaths(); + } + } + + private async Task ValidatePaths() + { + var queryFiles = new HashSet<string>(_pathQuerys.Count * 2); + foreach (var q in _pathQuerys) + { + switch (q.QueryPath) + { + case string s: queryFiles.Add(s); break; + case List<string> ls: + { + foreach (var p in ls) + { + queryFiles.Add(p); + } + break; + } + default: throw new NotSupportedException(); + } + } + + var files = new List<string>(queryFiles); + var res = await this.Assembly.Agent.QueryFileExistsAsync(new Luban.Common.Protos.QueryFilesExistsArg() { Root = RootDir, Files = files }); + var fileNotExistsSet = new HashSet<string>(queryFiles.Count * 2); + for (int i = 0; i < files.Count; i++) + { + if (!res.Exists[i]) + { + fileNotExistsSet.Add(files[i]); + } + } + + var agent = this.Assembly.Agent; + + foreach (var q in _pathQuerys) + { + switch (q.QueryPath) + { + case string s: + { + if (fileNotExistsSet.Contains(s)) + { + agent.Error("记录 {0} = {1} (来自文件:{2}) 所引用文件:{3} 不存在", q.DataPath, q.Value, q.Source, s); + } + break; + } + case List<string> ls: + { + if (ls.All(f => fileNotExistsSet.Contains(f))) + { + agent.Error("记录 {0} = {1} (来自文件:{2}) 所引用文件:{3} 不存在", q.DataPath, q.Value, q.Source, string.Join(',', ls)); + } + break; + } + default: throw new NotSupportedException(); + } + } + } + + private void ValidateTableModeIndex(DefTable table, List<DType> records) + { + var recordMap = new Dictionary<DType, DBean>(); + + switch (table.Mode) + { + case ETableMode.ONE: + { + if (records.Count != 1) + { + throw new Exception($"配置表 {table.FullName} 是单值表 mode=one,但数据个数:{records.Count} != 1"); + } + break; + } + case ETableMode.MAP: + { + foreach (DBean r in records) + { + DType key = r.Fields[table.IndexFieldIdIndex]; + if (!recordMap.TryAdd(key, r)) + { + throw new Exception($@"配置表 {table.FullName} 主键字段:{table.Index} 主键值:{key} 重复. + 记录1 来自文件:{DataUtil.GetSourceFile(r)} + 记录2 来自文件:{DataUtil.GetSourceFile(recordMap[key])} +"); + } + + } + break; + } + case ETableMode.BMAP: + { + var twoKeyMap = new Dictionary<(DType, DType), DBean>(); + foreach (DBean r in records) + { + DType key1 = r.Fields[table.IndexFieldIdIndex1]; + DType key2 = r.Fields[table.IndexFieldIdIndex2]; + if (!twoKeyMap.TryAdd((key1, key2), r)) + { + throw new Exception($@"配置表 {table.FullName} 主键字段:{table.Index} 主键值:({key1},{key2})重复. + 记录1 来自文件:{DataUtil.GetSourceFile(r)} + 记录2 来自文件:{DataUtil.GetSourceFile(twoKeyMap[(key1, key2)])} +"); + } + // 目前不支持 双key索引检查,但支持主key索引检查. + // 所以至少塞入一个,让ref检查能通过 + recordMap[key1] = r; + } + break; + } + } + table.Assembly.SetDataTableMap(table, recordMap); + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Validators/IValidator.cs b/src/Luban.Job.Cfg/Source/Validators/IValidator.cs new file mode 100644 index 0000000..021e667 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Validators/IValidator.cs @@ -0,0 +1,12 @@ +using Luban.Job.Cfg.Datas; +using Luban.Job.Cfg.Defs; + +namespace Luban.Job.Cfg.Validators +{ + public interface IValidator + { + void Compile(DefField def); + + void Validate(ValidatorContext ctx, DType data, bool nullable); + } +} diff --git a/src/Luban.Job.Cfg/Source/Validators/PathValidator.cs b/src/Luban.Job.Cfg/Source/Validators/PathValidator.cs new file mode 100644 index 0000000..9f32c2f --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Validators/PathValidator.cs @@ -0,0 +1,251 @@ +using Luban.Job.Cfg.Datas; +using Luban.Job.Cfg.Defs; +using Luban.Job.Cfg.Utils; +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace Luban.Job.Cfg.Validators +{ + interface IPathPattern + { + string Mode { get; } + + object CalcFinalPath(string path); + + bool EmptyAble { get; set; } + } + + class RegexPattern : IPathPattern + { + private readonly string _replacePattern; + + private readonly Regex _re; + + public bool EmptyAble { get; set; } + + public string Mode => "regex"; + + public RegexPattern(string matchPattern, string replacePattern) + { + _re = new Regex(matchPattern); + _replacePattern = replacePattern; + } + + public object CalcFinalPath(string path) + { + var finalPath = _re.Replace(path, _replacePattern); + if (finalPath == path && !_re.IsMatch(path)) + { + return null; + } + return finalPath; + } + } + + class SimpleReplacePattern : IPathPattern + { + private readonly string _prefix; + private readonly string _suffix; + + public bool EmptyAble { get; set; } + + public string Mode => "normal"; + + public SimpleReplacePattern(string prefix, string suffix) + { + _prefix = prefix; + _suffix = suffix; + } + + public object CalcFinalPath(string path) + { + return _prefix + path + _suffix; + } + } + + class Ue4ResourcePattern : IPathPattern + { + private readonly Regex _pat1; + private readonly Regex _pat2; + + public bool EmptyAble { get; set; } + + public string Mode => "ue"; + + public Ue4ResourcePattern() + { + _pat1 = new Regex(@"^/Game/(.+?)(\..+)?$"); + _pat2 = new Regex(@"^\w+'/Game/(.+?)(\..+)?'$"); + } + + private bool CheckMatch(Match match) + { + var groups = match.Groups; + if (!groups[1].Success) + { + return false; + } + if (groups[2].Success) + { + // 如果是 /Game/../xxx.yyy 的情形 + // 要求 yyy == xxx 或者 yyy == xxx_C + string path = groups[1].Value; + string suffix = groups[2].Value.Substring(1); + if (suffix.EndsWith("_C")) + { + suffix = suffix[0..^2]; + } + return path.EndsWith(suffix); + } + return true; + } + + private List<string> AlternativePaths(string rawPath) + { + return new List<string>() { rawPath + ".uasset", rawPath + ".umap" }; + } + + public object CalcFinalPath(string path) + { + var match1 = _pat1.Match(path); + if (match1.Success) + { + if (!CheckMatch(match1)) + { + return null; + } + return AlternativePaths(match1.Groups[1].Value); + } + var match2 = _pat2.Match(path); + if (match2.Success) + { + if (!CheckMatch(match2)) + { + return null; + } + return AlternativePaths(match2.Groups[1].Value); + } + return null; + } + } + + public class PathValidator : IValidator + { + public const string NAME = "path"; + + public string RawPattern { get; } + + internal IPathPattern PathPattern { get; private set; } + + public PathValidator(string pathPattern) + { + this.RawPattern = pathPattern; + } + + public void Validate(ValidatorContext ctx, DType data, bool nullable) + { + var assembly = ctx.Assembly; + + if (nullable && data == null) + { + return; + } + if (data is DString s) + { + string value = s.Value; + if (value == "" && PathPattern.EmptyAble) + { + return; + } + + string source = DataUtil.GetSourceFile(ValidatorContext.CurrentVisitor.CurrentValidateRecord); + object finalPaths = PathPattern.CalcFinalPath(value); + if (finalPaths == null) + { + assembly.Agent.Error("{0}:{1} (来自文件:{2}) 资源格式不合法", ValidatorContext.CurrentRecordPath, value, source); + return; + } + ctx.AddPathQuery(new PathQuery + { + Validator = this, + DataPath = ValidatorContext.CurrentRecordPath, + Value = value, + Source = source, + QueryPath = finalPaths + }); + return; + } + else + { + throw new ArgumentException($" path 检查只支持string, 但 {ValidatorContext.CurrentRecordPath} 不是string类型"); + } + } + + private void ThrowCompileError(DefField def, string err) + { + throw new System.ArgumentException($"{((DefBean)(def.HostType)).FullName} 字段:{def.Name} {RawPattern} 定义不合法. {err}"); + } + + public void Compile(DefField def) + { + string[] ss = RawPattern.Split(';'); + if (ss.Length < 1) + { + ThrowCompileError(def, ""); + } + + string patType = ss[0]; + bool emptyAble = false; + if (patType.EndsWith('?')) + { + patType = patType[0..^1]; + emptyAble = true; + } + + switch (patType) + { + case "normal": + { + if (ss.Length != 2) + { + ThrowCompileError(def, ""); + } + string pat = ss[1]; + int indexOfStar = pat.IndexOf('*'); + if (indexOfStar < 0) + { + ThrowCompileError(def, "必须包含 * "); + } + PathPattern = new SimpleReplacePattern(pat.Substring(0, indexOfStar), pat.Substring(indexOfStar + 1)); + break; + } + case "regex": + { + if (ss.Length != 3) + { + ThrowCompileError(def, "必须包含 pattern和replace"); + } + PathPattern = new RegexPattern(ss[1], ss[2]); + break; + } + case "ue": + { + if (ss.Length != 1) + { + ThrowCompileError(def, ""); + } + PathPattern = new Ue4ResourcePattern(); + break; + } + default: + { + ThrowCompileError(def, $"不支持的path模式类型:{patType}"); + break; + } + } + + PathPattern.EmptyAble = emptyAble; + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Validators/RangeValidator.cs b/src/Luban.Job.Cfg/Source/Validators/RangeValidator.cs new file mode 100644 index 0000000..e767300 --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Validators/RangeValidator.cs @@ -0,0 +1,243 @@ +using Luban.Job.Cfg.Datas; +using Luban.Job.Cfg.Defs; +using Luban.Job.Cfg.Utils; +using System; + +namespace Luban.Job.Cfg.Validators +{ + class RangeValidator : IValidator + { + public const string NAME = "range"; + + public string Name => NAME; + + private readonly string _str; + + private long? _min; + private long? _max; + + private double? _mind; + private double? _maxd; + + private bool _includeMinBound; + + private bool _includeMaxBound; + + public RangeValidator(string strRange) + { + _str = strRange.Trim(); + } + + private bool TryParse(string s, ref long? x) + { + s = s.Trim(); + if (string.IsNullOrEmpty(s)) + { + x = null; + return true; + } + else if (long.TryParse(s, out var v)) + { + x = v; + return true; + } + else + { + return false; + } + } + + private bool TryParse(string s, ref double? x) + { + s = s.Trim(); + if (string.IsNullOrEmpty(s)) + { + x = null; + return true; + } + else if (double.TryParse(s, out var v)) + { + x = v; + return true; + } + else + { + return false; + } + } + + public void Compile(DefField def) + { + void ThrowError() + { + throw new Exception($"结构:{ def.HostType.FullName } 字段: { def.Name} range 定义:{_str} 不合法"); + } + + if (_str.Length <= 2) + { + ThrowError(); + } + switch (_str[0]) + { + case '[': _includeMinBound = true; break; + case '(': _includeMinBound = false; break; + default: ThrowError(); break; + } + switch (_str[^1]) + { + case ']': _includeMaxBound = true; break; + case ')': _includeMaxBound = false; break; + default: ThrowError(); break; + } + + var pars = _str[1..^1].Split(','); + if (pars.Length != 2) + { + ThrowError(); + } + + bool p1 = TryParse(pars[0], ref _min); + bool p2 = TryParse(pars[0], ref _mind); + bool p3 = TryParse(pars[1], ref _max); + bool p4 = TryParse(pars[1], ref _maxd); + + if ((!p1 && !p2) || (!p3 && !p4)) + { + ThrowError(); + } + } + + + private bool CheckInLongRange(long x) + { + if (_min is long m && (_includeMinBound ? m > x : m >= x)) + { + return false; + } + if (_max is long n && (_includeMaxBound ? n < x : n <= x)) + { + return false; + } + return true; + } + + private bool CheckInDoubleRange(double x) + { + if (_mind is double m && (_includeMinBound ? m > x : m >= x)) + { + return false; + } + if (_maxd is double n && (_includeMaxBound ? n < x : n <= x)) + { + return false; + } + return true; + } + + public string Source => DataUtil.GetSourceFile(ValidatorContext.CurrentVisitor.CurrentValidateRecord); + + public void Validate(ValidatorContext ctx, DType data, bool nullable) + { + var assembly = ctx.Assembly; + void LogError() + { + assembly.Agent.Error("记录 {0}:{1} (来自文件:{2}) 不在范围:{3}内", ValidatorContext.CurrentRecordPath, data, Source, _str); + } + + if (nullable && data == null) + { + return; + } + + switch (data) + { + case DByte b: + { + if (!CheckInLongRange(b.Value)) + { + LogError(); + return; + } + break; + } + case DFshort s: + { + if (!CheckInLongRange(s.Value)) + { + LogError(); + return; + } + break; + } + case DShort s: + { + if (!CheckInLongRange(s.Value)) + { + LogError(); + return; + } + break; + } + case DInt i: + { + if (!CheckInLongRange(i.Value)) + { + LogError(); + return; + } + break; + } + case DFint i: + { + if (!CheckInLongRange(i.Value)) + { + LogError(); + return; + } + break; + } + case DLong l: + { + if (!CheckInLongRange(l.Value)) + { + LogError(); + return; + } + break; + } + case DFlong fl: + { + if (!CheckInLongRange(fl.Value)) + { + LogError(); + return; + } + break; + } + case DFloat ff: + { + if (!CheckInDoubleRange(ff.Value)) + { + LogError(); + return; + } + break; + } + case DDouble dd: + { + if (!CheckInDoubleRange(dd.Value)) + { + LogError(); + return; + } + break; + } + default: + { + assembly.Agent.Error("记录 {0}:{1} (来自文件:{2}) 不支持 range", ValidatorContext.CurrentRecordPath, data, Source); + return; + } + } + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Validators/RefValidator.cs b/src/Luban.Job.Cfg/Source/Validators/RefValidator.cs new file mode 100644 index 0000000..0c8e0ed --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Validators/RefValidator.cs @@ -0,0 +1,89 @@ +using Luban.Job.Cfg.Datas; +using Luban.Job.Cfg.DataVisitors; +using Luban.Job.Cfg.Defs; +using Luban.Job.Cfg.Utils; +using System; +using System.Collections.Generic; + +namespace Luban.Job.Cfg.Validators +{ + public class RefValidator : IValidator + { + public const string NAME = "ref"; + + public List<string> Tables { get; } + + public string FirstTable => GetActualTableName(Tables[0]); + + public static string GetActualTableName(string table) + { + return table.EndsWith("?") ? table[0..^1] : table; + } + + public RefValidator(List<string> tables) + { + this.Tables = new List<string>(tables); + } + + public void Validate(ValidatorContext ctx, DType key, bool nullable) + { + // 对于可空字段,跳过检查 + if (nullable && key == null) + { + return; + } + var assembly = ctx.Assembly; + + foreach (var table in Tables) + { + bool zeroAble; + string actualTable; + if (table.EndsWith("?")) + { + zeroAble = true; + actualTable = table[0..^1]; + } + else + { + zeroAble = false; + actualTable = table; + } + if (zeroAble && key.Apply(IsDefaultValue.Ins)) + { + return; + } + DefTable ct = assembly.GetCfgTable(actualTable); + var recordMap = assembly.GetTableDataMap(ct); + if (recordMap != null && !recordMap.ContainsKey(key)) + { + string source = DataUtil.GetSourceFile(ValidatorContext.CurrentVisitor.CurrentValidateRecord); + assembly.Agent.Error("记录 {0} = {1} (来自文件:{2}) 在引用表:{3} 中不存在", ValidatorContext.CurrentRecordPath, key, source, table); + } + } + + } + + public void Compile(DefField def) + { + if (Tables.Count == 0) + { + throw new Exception($"结构:{ def.HostType.FullName } 字段: { def.Name} ref 不能为空"); + } + + foreach (var table in Tables) + { + string actualTable = table.EndsWith("?") ? table[0..^1] : table; + var ct = def.Assembly.GetCfgTable(actualTable); + if (ct == null) + { + throw new Exception($"结构:{def.HostType.FullName} 字段:{def.Name} ref:{table} 不存在"); + } + if (ct.IsOneValueTable) + { + throw new Exception($"结构:{def.HostType.FullName} 字段:{def.Name} ref:{table} 是单值表,不能执行引用检查"); + } + } + + } + } +} diff --git a/src/Luban.Job.Cfg/Source/Validators/ValidatorFactory.cs b/src/Luban.Job.Cfg/Source/Validators/ValidatorFactory.cs new file mode 100644 index 0000000..88e9c1a --- /dev/null +++ b/src/Luban.Job.Cfg/Source/Validators/ValidatorFactory.cs @@ -0,0 +1,32 @@ +using Luban.Config.Common.RawDefs; +using System; +using System.Collections.Generic; + +namespace Luban.Job.Cfg.Validators +{ + static class ValidatorFactory + { + private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger(); + public static IValidator Create(Validator validator) + { + s_logger.Debug("== create validator {type}:{rule}", validator.Type, validator.Rule); + switch (validator.Type) + { + case RefValidator.NAME: + { + return new RefValidator(new List<string> { validator.Rule }); + } + case PathValidator.NAME: + { + return new PathValidator(validator.Rule);//.Split(',').ToList()); + } + case RangeValidator.NAME: + { + return new RangeValidator(validator.Rule); + } + default: + throw new NotSupportedException("unknown validator type:" + validator.Type); + } + } + } +} diff --git a/src/Luban.Job.Common/Luban.Job.Common.csproj b/src/Luban.Job.Common/Luban.Job.Common.csproj new file mode 100644 index 0000000..0c61ba4 --- /dev/null +++ b/src/Luban.Job.Common/Luban.Job.Common.csproj @@ -0,0 +1,22 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <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="CommandLineParser" Version="2.8.0" /> + <PackageReference Include="Scriban" Version="2.1.4" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\Luban.Server.Common\Luban.Server.Common.csproj" /> + </ItemGroup> + +</Project> diff --git a/src/Luban.Job.Common/Source/ConstStrings.cs b/src/Luban.Job.Common/Source/ConstStrings.cs new file mode 100644 index 0000000..65ff655 --- /dev/null +++ b/src/Luban.Job.Common/Source/ConstStrings.cs @@ -0,0 +1,9 @@ +namespace Luban.Job.Common +{ + public static class ConstStrings + { + public const string CsList = "System.Collections.Generic.List"; + public const string CsHashSet = "System.Collections.Generic.HashSet"; + public const string CsHashMap = "System.Collections.Generic.Dictionary"; + } +} diff --git a/src/Luban.Job.Common/Source/Defs/CommonDefLoader.cs b/src/Luban.Job.Common/Source/Defs/CommonDefLoader.cs new file mode 100644 index 0000000..d3fd4a3 --- /dev/null +++ b/src/Luban.Job.Common/Source/Defs/CommonDefLoader.cs @@ -0,0 +1,360 @@ +using Luban.Common.Utils; +using Luban.Job.Common.RawDefs; +using Luban.Server.Common; +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Luban.Job.Common.Defs +{ + public class LoadDefException : Exception + { + public LoadDefException() + { + } + + public LoadDefException(string message) : base(message) + { + } + + public LoadDefException(string message, Exception innerException) : base(message, innerException) + { + } + + protected LoadDefException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + public abstract class CommonDefLoader + { + private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger(); + + protected RemoteAgent Agent { get; } + + public string RootDir { get; private set; } + + public bool IsBeanDefaultCompatible { get; protected set; } + + public bool IsBeanFieldMustDefineId { get; protected set; } + + private readonly Dictionary<string, Action<XElement>> _rootDefineHandlers = new Dictionary<string, Action<XElement>>(); + private readonly Dictionary<string, Action<XElement>> _moduleDefineHandlers = new Dictionary<string, Action<XElement>>(); + + protected readonly Stack<string> _namespaceStack = new Stack<string>(); + protected readonly Stack<string> _importFileStack = new Stack<string>(); + protected readonly Stack<XElement> _defineStack = new Stack<XElement>(); + + protected string TopModule { get; private set; } + + protected readonly List<Const> _consts = new List<Const>(); + protected readonly List<PEnum> _enums = new List<PEnum>(); + protected readonly List<Bean> _beans = new List<Bean>(); + + protected CommonDefLoader(RemoteAgent agent) + { + Agent = agent; + + _rootDefineHandlers.Add("topmodule", SetTopModule); + + _moduleDefineHandlers.Add("module", AddModule); + _moduleDefineHandlers.Add("const", AddConst); + _moduleDefineHandlers.Add("enum", AddEnum); + _moduleDefineHandlers.Add("bean", AddBean); + } + + private string _rootXml; + + public async Task LoadAsync(string rootXml) + { + _rootXml = rootXml; + + RootDir = FileUtil.GetParent(rootXml); + + XElement doc = await Agent.OpenXmlAsync(rootXml); + + foreach (XElement e in doc.Elements()) + { + var tagName = e.Name.LocalName; + if (tagName == "import") + { + await AddImportAsync(XmlUtil.GetRequiredAttribute(e, "name")); + continue; + } + if (_rootDefineHandlers.TryGetValue(tagName, out var handler)) + { + handler(e); + } + else + { + throw new LoadDefException($"定义文件:{rootXml} 非法 tag:{tagName}"); + } + } + } + + protected void RegisterRootDefineHandler(string name, Action<XElement> handler) + { + _rootDefineHandlers.Add(name, handler); + } + + protected void RegisterModuleDefineHandler(string name, Action<XElement> handler) + { + _moduleDefineHandlers.Add(name, handler); + } + + protected string CurNamespace => _namespaceStack.Peek(); + + protected string CurImportFile => _importFileStack.Peek(); + + protected XElement CurDefine => _defineStack.Peek(); + + #region root handler + + private void SetTopModule(XElement e) + { + this.TopModule = XmlUtil.GetRequiredAttribute(e, "name"); + } + + private async Task AddImportAsync(string xmlFile) + { + var rootFileName = FileUtil.GetFileName(_rootXml); + + var xmlFullPath = FileUtil.Combine(RootDir, xmlFile); + s_logger.Trace("import {file} {full_path}", xmlFile, xmlFullPath); + + var fileOrDirContent = await Agent.GetFileOrDirectoryAsync(xmlFullPath); + + if (fileOrDirContent.IsFile) + { + s_logger.Trace("== file:{file}", xmlFullPath); + _importFileStack.Push(xmlFullPath); + AddModule(XmlUtil.Open(xmlFullPath, await Agent.GetFromCacheOrReadAllBytesAsync(xmlFullPath, fileOrDirContent.Md5))); + _importFileStack.Pop(); + } + else + { + // 如果是目录,则递归导入目录下的所有 .xml 定义文件 + foreach (var subFile in fileOrDirContent.SubFiles) + { + var subFileName = subFile.FilePath; + s_logger.Trace("sub import xmlfile:{file} root file:{root}", subFileName, rootFileName); + // 有时候 root 定义文件会跟 module定义文件放在一个目录. 当以目录形式导入子module时,不希望导入它 + if (FileUtil.GetFileName(subFileName) == rootFileName) + { + s_logger.Trace("ignore import root file:{root}", subFileName); + continue; + } + string subFullPath = subFileName; + _importFileStack.Push(subFullPath); + AddModule(XmlUtil.Open(subFullPath, await Agent.GetFromCacheOrReadAllBytesAsync(subFullPath, subFile.MD5))); + _importFileStack.Pop(); + } + } + } + + #endregion + + + #region module handler + + private void AddModule(XElement me) + { + var name = XmlUtil.GetRequiredAttribute(me, "name"); + if (string.IsNullOrEmpty(name)) + { + throw new LoadDefException($"xml:{CurImportFile} contains module which's name is empty"); + } + + _namespaceStack.Push(_namespaceStack.Count > 0 ? TypeUtil.MakeFullName(_namespaceStack.Peek(), name) : name); + + // 加载所有module定义,允许嵌套 + foreach (XElement e in me.Elements()) + { + var tagName = e.Name.LocalName; + if (_moduleDefineHandlers.TryGetValue(tagName, out var handler)) + { + if (tagName != "module") + { + _defineStack.Push(e); + handler(e); + _defineStack.Pop(); + } + else + { + handler(e); + } + } + else + { + throw new LoadDefException($"定义文件:{CurImportFile} module:{CurNamespace} 不支持 tag:{tagName}"); + } + } + _namespaceStack.Pop(); + } + + private static readonly List<string> _fieldRequireAttrs = new List<string> { "name", "id", "type", }; + + protected virtual Field CreateField(XElement e) + { + ValidAttrKeys(e, null, _fieldRequireAttrs); + var f = new Field() + { + Id = XmlUtil.GetRequiredIntAttribute(e, "id"), + Name = XmlUtil.GetRequiredAttribute(e, "name"), + Type = CreateType(e, "type"), + }; + return f; + } + + protected void AddBean(XElement e) + { + AddBean(e, ""); + } + + private static readonly List<string> _beanOptinsAttrs1 = new List<string> { "compatible", "value_type" }; + private static readonly List<string> _beanRequireAttrs1 = new List<string> { "id", "name" }; + + private static readonly List<string> _beanOptinsAttrs2 = new List<string> { "id", "compatible", "value_type" }; + private static readonly List<string> _beanRequireAttrs2 = new List<string> { "name" }; + + protected virtual void AddBean(XElement e, string parent) + { + if (IsBeanFieldMustDefineId) + { + ValidAttrKeys(e, _beanOptinsAttrs1, _beanRequireAttrs1); + } + else + { + ValidAttrKeys(e, _beanOptinsAttrs2, _beanRequireAttrs2); + } + var b = new Bean() + { + Name = XmlUtil.GetRequiredAttribute(e, "name"), + Namespace = CurNamespace, + Parent = parent.Length > 0 ? parent : "", + TypeId = XmlUtil.GetOptionIntAttribute(e, "id"), + IsSerializeCompatible = XmlUtil.GetOptionBoolAttribute(e, "compatible", IsBeanDefaultCompatible), + IsValueType = XmlUtil.GetOptionBoolAttribute(e, "value_type"), + }; + 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); + } + } + + protected static string CreateType(XElement e, string key) + { + return XmlUtil.GetRequiredAttribute(e, key); + } + + protected void ValidAttrKeys(XElement e, List<string> optionKeys, List<string> requireKeys) + { + foreach (var k in e.Attributes()) + { + var name = k.Name.LocalName; + if (!requireKeys.Contains(name) && (optionKeys != null && !optionKeys.Contains(name))) + { + throw new LoadDefException($"定义文件:{CurImportFile} module:{CurNamespace} 定义:{e} 包含未知属性 attr:{name}"); + } + } + foreach (var k in requireKeys) + { + if (e.Attribute(k) == null) + { + throw new LoadDefException($"定义文件:{CurImportFile} module:{CurNamespace} 定义:{e} 缺失属性 attr:{k}"); + } + } + } + + + private static readonly List<string> _constRequiredAttrs = new List<string> { "name" }; + private static readonly List<string> _constOptionalItemAttrs = new List<string> { "value" }; + private static readonly List<string> _constItemRequiredAttrs = new List<string> { "name", "type" }; + + protected void AddConst(XElement e) + { + ValidAttrKeys(e, null, _constRequiredAttrs); + var c = new Const() + { + Name = XmlUtil.GetRequiredAttribute(e, "name"), + Namespace = CurNamespace, + }; + foreach (XElement item in e.Elements()) + { + ValidAttrKeys(item, _constOptionalItemAttrs, _constItemRequiredAttrs); + c.Items.Add(new ConstItem() + { + Name = XmlUtil.GetRequiredAttribute(item, "name"), + Type = CreateType(item, "type"), + Value = XmlUtil.GetRequiredAttribute(item, "value"), + }); + } + s_logger.Trace("add const {@const}", c); + _consts.Add(c); + } + + private static readonly List<string> _enumOptionalAttrs = new List<string> { "flags" }; + private static readonly List<string> _enumRequiredAttrs = new List<string> { "name" }; + private static readonly List<string> _enumOptionalItemAttrs = new List<string> { "value", "alias" }; + private static readonly List<string> _enumItemRequiredAttrs = new List<string> { "name" }; + + protected void AddEnum(XElement e) + { + ValidAttrKeys(e, _enumOptionalAttrs, _enumRequiredAttrs); + var en = new PEnum() + { + Name = XmlUtil.GetRequiredAttribute(e, "name"), + Namespace = CurNamespace, + IsFlags = XmlUtil.GetOptionBoolAttribute(e, "flags"), + }; + + foreach (XElement item in e.Elements()) + { + ValidAttrKeys(item, _enumOptionalItemAttrs, _enumItemRequiredAttrs); + en.Items.Add(new EnumItem() + { + Name = XmlUtil.GetRequiredAttribute(item, "name"), + Alias = XmlUtil.GetOptionalAttribute(item, "alias"), + Value = XmlUtil.GetOptionalAttribute(item, "value"), + }); + } + s_logger.Trace("add enum:{@enum}", en); + _enums.Add(en); + } + #endregion + } +} diff --git a/src/Luban.Job.Common/Source/Defs/DefAssemblyBase.cs b/src/Luban.Job.Common/Source/Defs/DefAssemblyBase.cs new file mode 100644 index 0000000..88d508d --- /dev/null +++ b/src/Luban.Job.Common/Source/Defs/DefAssemblyBase.cs @@ -0,0 +1,187 @@ + +using Luban.Common.Utils; +using Luban.Job.Common.Types; +using Luban.Server.Common; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Luban.Job.Common.Defs +{ + public abstract class DefAssemblyBase + { + private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger(); + + public Dictionary<string, DefTypeBase> Types { get; } = new Dictionary<string, DefTypeBase>(); + + public RemoteAgent Agent { get; protected set; } + + public string TopModule { get; protected set; } + + public bool SupportDatetimeType { get; protected set; } + + public void AddType(DefTypeBase type) + { + string fullName = type.FullName; + if (Types.ContainsKey(fullName)) + { + throw new Exception($"type:{fullName} duplicate"); + } + Types.Add(fullName, type); + } + + public DefTypeBase GetDefType(string fullName) + { + return Types.TryGetValue(fullName, out var type) ? type : null; + } + + public DefTypeBase GetDefType(string module, string type) + { + if (Types.TryGetValue(type, out var t)) + { + return t; + } + else if (Types.TryGetValue(TypeUtil.MakeFullName(module, type), out t)) + { + return t; + } + else + { + return null; + } + } + + private readonly Dictionary<(DefTypeBase, bool), TType> cacheDefTTypes = new Dictionary<(DefTypeBase, bool), TType>(); + + protected TType GetOrCreateTEnum(DefEnum defType, bool nullable) + { + if (cacheDefTTypes.TryGetValue((defType, nullable), out var t)) + { + return t; + } + else + { + return cacheDefTTypes[(defType, nullable)] = new TEnum(defType, nullable); + } + } + + protected TType GetOrCreateTBean(DefTypeBase defType, bool nullable) + { + if (cacheDefTTypes.TryGetValue((defType, nullable), out var t)) + { + return t; + } + else + { + return cacheDefTTypes[(defType, nullable)] = new TBean((DefBeanBase)defType, nullable); + } + } + + public TType GetDefTType(string module, string type, bool nullable) + { + var defType = GetDefType(module, type); + switch (defType) + { + case DefBeanBase d: return GetOrCreateTBean(d, nullable); + case DefEnum d: return GetOrCreateTEnum(d, nullable); + default: return null; + } + } + + public List<T> GetDefTypesByType<T>() where T : DefTypeBase + { + return Types.Values.Where(v => typeof(T).IsAssignableFrom(v.GetType())).Select(v => (T)v).ToList(); + } + + public TType CreateType(string module, string type) + { + int sepIndex = type.IndexOf(',', System.StringComparison.Ordinal); + if (sepIndex > 0) + { + string containerType = type.Substring(0, sepIndex).Trim(); + return CreateContainerType(module, containerType, type.Substring(sepIndex + 1, type.Length - sepIndex - 1).Trim()); + } + else + { + return CreateNotContainerType(module, type); + } + } + + protected TType CreateNotContainerType(string module, string type) + { + bool nullable; + if (type.EndsWith('?')) + { + nullable = true; + type = type[0..^1]; + } + else + { + nullable = false; + } + switch (type) + { + case "bool": return nullable ? TBool.NullableIns : TBool.Ins; + case "byte": return nullable ? TByte.NullableIns : TByte.Ins; + case "short": return nullable ? TShort.NullableIns : TShort.Ins; + case "fshort": return nullable ? TFshort.NullableIns : TFshort.Ins; + case "int": return nullable ? TInt.NullableIns : TInt.Ins; + case "fint": return nullable ? TFint.NullableIns : TFint.Ins; + case "long": return nullable ? TLong.NullableIns : TLong.Ins; + case "flong": return nullable ? TFlong.NullableIns : TFlong.Ins; + case "float": return nullable ? TFloat.NullableIns : TFloat.Ins; + case "double": return nullable ? TDouble.NullableIns : TDouble.Ins; + case "bytes": return TBytes.Ins; + case "string": return nullable ? TString.NullableIns : TString.Ins; + case "text": return nullable ? TText.NullableIns : TText.Ins; + case "vector2": return nullable ? TVector2.NullableIns : TVector2.Ins; + case "vector3": return nullable ? TVector3.NullableIns : TVector3.Ins; + case "vector4": return nullable ? TVector4.NullableIns : TVector4.Ins; + case "datetime": return SupportDatetimeType ? (nullable ? TDateTime.NullableIns : TDateTime.Ins) : throw new NotSupportedException($"只有配置支持datetime数据类型"); + default: + { + var dtype = GetDefTType(module, type, nullable); + if (dtype != null) + { + return dtype; + } + else + { + throw new ArgumentException($"invalid type. module:{module} type:{type}"); + } + } + } + } + + protected TMap CreateMapType(string module, string keyValueType, bool isTreeMap) + { + string[] elementTypes = keyValueType.Split(',').Select(s => s.Trim()).ToArray(); + if (elementTypes.Length != 2) + { + throw new ArgumentException($"invalid map element type: {keyValueType}"); + } + return new TMap(CreateNotContainerType(module, elementTypes[0]), CreateNotContainerType(module, elementTypes[1]), isTreeMap); + } + + protected TType CreateContainerType(string module, string containerType, string elementType) + { + switch (containerType) + { + case "array": return new TArray(CreateNotContainerType(module, elementType)); + case "list": return new TList(CreateNotContainerType(module, elementType), false); + case "linkedlist": return new TList(CreateNotContainerType(module, elementType), true); + case "arraylist": return new TList(CreateNotContainerType(module, elementType), false); + case "set": return new TSet(CreateNotContainerType(module, elementType), false); + case "hashset": return new TSet(CreateNotContainerType(module, elementType), true); + case "treeset": return new TSet(CreateNotContainerType(module, elementType), false); + case "map": return CreateMapType(module, elementType, false); + case "treemap": return CreateMapType(module, elementType, true); + case "hashmap": return CreateMapType(module, elementType, false); + default: + { + throw new ArgumentException($"invalid container type. module:{module} container:{containerType} element:{elementType}"); + } + } + } + } +} diff --git a/src/Luban.Job.Common/Source/Defs/DefBeanBase.cs b/src/Luban.Job.Common/Source/Defs/DefBeanBase.cs new file mode 100644 index 0000000..7b13e5d --- /dev/null +++ b/src/Luban.Job.Common/Source/Defs/DefBeanBase.cs @@ -0,0 +1,170 @@ +using Luban.Job.Common.RawDefs; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Luban.Job.Common.Defs +{ + public abstract class DefBeanBase : DefTypeBase + { + public bool IsBean => true; + + public string Parent { get; } + + public bool IsValueType { get; } + + public DefBeanBase ParentDefType { get; protected set; } + + public DefBeanBase RootDefType => this.ParentDefType == null ? this : this.ParentDefType.RootDefType; + + public bool IsSerializeCompatible { get; } + + public List<DefBeanBase> Children { get; set; } + + public List<DefBeanBase> HierarchyNotAbstractChildren { get; set; } + + public bool IsNotAbstractType => Children == null; + + public bool IsAbstractType => Children != null; + + public List<DefFieldBase> HierarchyFields { get; private set; } = new List<DefFieldBase>(); + + public List<DefFieldBase> Fields { get; } = new List<DefFieldBase>(); + + public string CsClassModifier => IsAbstractType ? "abstract" : "sealed"; + + public string CsMethodModifier => ParentDefType != null ? "override" : (IsAbstractType ? "virtual" : ""); + + + public string JavaClassModifier => IsAbstractType ? "abstract" : "final"; + + public string JavaMethodModifier => ParentDefType != null ? "override" : (IsAbstractType ? "virtual" : ""); + + public DefBeanBase(Bean b) + { + Name = b.Name; + Namespace = b.Namespace; + Parent = b.Parent; + Id = b.TypeId; + IsValueType = b.IsValueType; + foreach (var field in b.Fields) + { + Fields.Add(CreateField(field, 0)); + } + } + + protected abstract DefFieldBase CreateField(Field f, int idOffset); + + public void CollectHierarchyNotAbstractChildren(List<DefBeanBase> children) + { + if (IsAbstractType) + { + foreach (var c in Children) + { + c.CollectHierarchyNotAbstractChildren(children); + } + } + else + { + children.Add(this); + } + } + + public virtual DefBeanBase GetNotAbstractChildType(string typeNameOrAliasName) + { + if (string.IsNullOrWhiteSpace(typeNameOrAliasName)) + { + return null; + } + foreach (var c in HierarchyNotAbstractChildren) + { + if (c.Name == typeNameOrAliasName) + { + return c; + } + } + return null; + } + + internal DefFieldBase GetField(string index) + { + return HierarchyFields.Where(f => f.Name == index).FirstOrDefault(); + } + + public bool TryGetField(string index, out DefFieldBase field, out int fieldIndexId) + { + for (int i = 0; i < HierarchyFields.Count; i++) + { + if (HierarchyFields[i].Name == index) + { + field = HierarchyFields[i]; + fieldIndexId = i; + return true; + } + } + field = null; + fieldIndexId = 0; + return false; + } + + protected void CollectHierarchyFields(List<DefFieldBase> fields) + { + if (ParentDefType != null) + { + ParentDefType.CollectHierarchyFields(fields); + } + fields.AddRange(Fields); + } + + public override void PreCompile() + { + if (!string.IsNullOrEmpty(Parent)) + { + if ((ParentDefType = (DefBeanBase)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); + } + + public override void Compile() + { + var cs = new List<DefBeanBase>(); + if (Children != null) + { + CollectHierarchyNotAbstractChildren(cs); + } + HierarchyNotAbstractChildren = cs; + + + var ids = new HashSet<int>(); + foreach (var c in cs) + { + if (c.Id == 0) + { + throw new Exception($"bean:{FullName} is child of dynamic type. beanid:{Id} can't be 0!"); + } + if (!ids.Add(c.Id)) + { + throw new Exception($"bean:{c.FullName} beanid:{c.Id} duplicate!"); + } + } + DefFieldBase.CompileFields(this, HierarchyFields, true); + } + + public override void PostCompile() + { + foreach (var field in HierarchyFields) + { + field.PostCompile(); + } + } + } +} diff --git a/src/Luban.Job.Common/Source/Defs/DefConst.cs b/src/Luban.Job.Common/Source/Defs/DefConst.cs new file mode 100644 index 0000000..053cad2 --- /dev/null +++ b/src/Luban.Job.Common/Source/Defs/DefConst.cs @@ -0,0 +1,61 @@ +using Luban.Job.Common.RawDefs; +using Luban.Job.Common.Types; +using System; +using System.Collections.Generic; + +namespace Luban.Job.Common.Defs +{ + public class DefConst : DefTypeBase + { + public class Item + { + public string Name { get; set; } + + public string Type { get; set; } + + public string Value { get; set; } + + public TType CType { get; set; } + } + + public List<Item> Items { get; set; } = new List<Item>(); + + public DefConst(Const c) + { + Namespace = c.Namespace; + Name = c.Name; + + foreach (var item in c.Items) + { + Items.Add(new Item { Name = item.Name, Type = item.Type, Value = item.Value }); + } + } + + public override void Compile() + { + var FullName = this.FullName; + HashSet<string> names = new HashSet<string>(); + + foreach (var item in Items) + { + if (item.Name.Length == 0) + { + throw new Exception($"{FullName} 常量字段名不能为空"); + } + if (!names.Add(item.Name)) + { + throw new Exception($"{FullName} 字段名:{item.Name} 重复"); + } + if ((item.CType = AssemblyBase.CreateType(Namespace, item.Type)) == null) + { + throw new Exception($"{FullName} type:{item.Type} 类型不存在"); + } + if (!item.CType.TryParseFrom(item.Value)) + { + throw new Exception($"{FullName} value:{item.Value} 不是合法的 type:{item.Type} 类型值"); + } + } + } + + } +} diff --git a/src/Luban.Job.Common/Source/Defs/DefEnum.cs b/src/Luban.Job.Common/Source/Defs/DefEnum.cs new file mode 100644 index 0000000..9ff5c84 --- /dev/null +++ b/src/Luban.Job.Common/Source/Defs/DefEnum.cs @@ -0,0 +1,148 @@ +using Luban.Job.Common.RawDefs; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Luban.Job.Common.Defs +{ + + public class DefEnum : DefTypeBase + { + public class Item + { + public string Name { get; set; } + + public string Value { get; set; } + + public string Alias { get; set; } + + public string AliasOrName => string.IsNullOrWhiteSpace(Alias) ? Name : Alias; + + public int IntValue { get; set; } + } + + public bool IsFlags { get; set; } + + public bool IsUniqueItemId { get; set; } + + public List<Item> Items { get; set; } = new List<Item>(); + + private readonly Dictionary<string, int> _nameOrAlias2Value = new Dictionary<string, int>(); + + public bool TryValueByNameOrAlias(string name, out int value) + { + return _nameOrAlias2Value.TryGetValue(name, out value); + } + + + public int GetValueByNameOrAlias(string name) + { + // TODO flags ? + if (!name.Contains('|')) + { + return GetBasicValueByNameOrAlias(name); + } + int combindValue = 0; + foreach (var s in name.Split('|')) + { + combindValue |= GetBasicValueByNameOrAlias(s.Trim()); + } + return combindValue; + } + + private int GetBasicValueByNameOrAlias(string name) + { + if (_nameOrAlias2Value.TryGetValue(name, out var value)) + { + return value; + } + else + { + throw new Exception($"{name} 不是有效 枚举:{FullName} 值"); + } + } + + public DefEnum(PEnum e) + { + Name = e.Name; + Namespace = e.Namespace; + IsFlags = e.IsFlags; + IsUniqueItemId = e.IsUniqueItemId; + + foreach (var item in e.Items) + { + Items.Add(new Item { Name = item.Name, Alias = item.Alias, Value = item.Value }); + } + } + + public override void Compile() + { + var fullName = FullName; + + int lastEnumValue = -1; + var names = new HashSet<string>(); + foreach (var item in Items) + { + string value = item.Value.ToLower(); + if (!names.Add(item.Name)) + { + throw new Exception($"enum:{fullName} 字段:{item.Name} 重复"); + } + if (string.IsNullOrEmpty(value)) + { + // A, + item.IntValue = ++lastEnumValue; + item.Value = item.IntValue.ToString(); + } + else if (int.TryParse(item.Value, out var v)) + { + // A = 5, + item.IntValue = v; + lastEnumValue = v; + } + else if (value.StartsWith("0x")) + { + + if (int.TryParse(value.Substring(2), System.Globalization.NumberStyles.HexNumber, null, out var x)) + { + item.IntValue = x; + lastEnumValue = x; + } + else + { + throw new Exception($"enum:{fullName} 枚举名:{item.Name} value:{item.Value} 非法"); + } + } + else if (IsFlags) + { + // D = A | B | C, + string[] itemNames = item.Value.Split('|').Select(s => s.Trim()).ToArray(); + foreach (var n in itemNames) + { + var index = Items.FindIndex(i => i.Name == n); + if (index < 0) + { + throw new Exception($"enum:{fullName} 枚举名:{item.Name} 值:{item.Value} 非法"); + } + item.IntValue |= Items[index].IntValue; + } + } + else + { + throw new Exception($"enum:{fullName} 枚举名:{item.Name} value:{item.Value} 非法"); + } + + if (!string.IsNullOrWhiteSpace(item.Name) && !_nameOrAlias2Value.TryAdd(item.Name, item.IntValue)) + { + throw new Exception($"enum:{fullName} 枚举名:{Name} 重复"); + } + + if (!string.IsNullOrWhiteSpace(item.Alias) && !_nameOrAlias2Value.TryAdd(item.Alias, item.IntValue)) + { + throw new Exception($"enum:{fullName} 枚举名:{Name} alias:{item.Alias} 重复"); + } + } + } + + } +} diff --git a/src/Luban.Job.Common/Source/Defs/DefFieldBase.cs b/src/Luban.Job.Common/Source/Defs/DefFieldBase.cs new file mode 100644 index 0000000..b735d56 --- /dev/null +++ b/src/Luban.Job.Common/Source/Defs/DefFieldBase.cs @@ -0,0 +1,167 @@ +using Luban.Common.Utils; +using Luban.Job.Common.RawDefs; +using Luban.Job.Common.Types; +using System; +using System.Collections.Generic; + +namespace Luban.Job.Common.Defs +{ + public abstract class DefFieldBase + { + public DefAssemblyBase AssemblyBase => HostType.AssemblyBase; + + public DefTypeBase HostType { get; set; } + + public int Id { get; set; } + + public string Name { get; set; } + + private string _lanStyleName; + public string CsStyleName + { + get + { + if (_lanStyleName == null) + { + _lanStyleName = TypeUtil.ToCsStyleName(Name); + } + return _lanStyleName; + } + } + + public string JavaStyleName + { + get + { + if (_lanStyleName == null) + { + _lanStyleName = TypeUtil.ToJavaStyleName(Name); + } + return _lanStyleName; + } + } + + public string CppStyleName => JavaStyleName; + + public string TsStyleName + { + get + { + if (_lanStyleName == null) + { + _lanStyleName = TypeUtil.ToJavaStyleName(Name); + } + return _lanStyleName; + } + } + + public string PyStyleName => Name; + + public string Type { get; } + + public TType CType { get; protected set; } + + public bool IsNullable => CType.IsNullable; + + public string UpperCaseName => Name.ToUpper(); + + + public DefFieldBase(DefTypeBase host, Field f, int idOffset) + { + HostType = host; + Id = f.Id + idOffset; + Name = f.Name; + Type = f.Type; + } + + public virtual void Compile() + { + + if (Id < 0 || Id > 256) + { + throw new Exception($"结构:{HostType.FullName} 字段:{Name} id:{Id} 超出范围"); + } + if (!TypeUtil.IsValidName(Name)) + { + throw new Exception($"filed name:{Name} is reserved"); + } + + if ((CType = AssemblyBase.CreateType(HostType.Namespace, Type)) == null) + { + throw new Exception($"type:{HostType.FullName} filed:{Name} type:{Type} is invalid"); + } + + //if (IsNullable && (CType.IsCollection || (CType is TBean))) + //{ + // throw new Exception($"type:{HostType.FullName} filed:{Name} type:{Type} is collection or bean. not support nullable"); + //} + + 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; + } + } + } + + public virtual void PostCompile() + { + // 检查 字段类型 与 所引用的表的key是否一致 + } + + public static void CompileFields<T>(DefTypeBase hostType, List<T> fields, bool verifyId) where T : DefFieldBase + { + if (verifyId) + { + var ids = new HashSet<int>(); + foreach (var f in fields) + { + if (f.Id == 0) + { + throw new Exception($"type:{hostType.FullName} field:{f.Name} id can't be 0"); + } + if (!ids.Add(f.Id)) + { + throw new Exception($"type:{hostType.FullName} field:{f.Name} id:{f.Id} duplicate"); + } + } + } + + var names = new HashSet<string>(); + foreach (var f in fields) + { + var fname = f.Name; + if (fname.Length == 0) + { + throw new Exception($"type:{hostType.FullName} field id:{f.Id} name can't be empty"); + } + if (!names.Add(fname)) + { + throw new Exception($"type:{hostType.FullName} field:{fname} duplicate"); + } + if (TypeUtil.ToCsStyleName(fname) == hostType.Name) + { + throw new Exception($"type:{hostType.FullName} 字段:{fname} 生成的c#字段名与类型名相同,会引起编译错误"); + } + } + + foreach (var f in fields) + { + f.Compile(); + } + } + } +} diff --git a/src/Luban.Job.Common/Source/Defs/DefTypeBase.cs b/src/Luban.Job.Common/Source/Defs/DefTypeBase.cs new file mode 100644 index 0000000..6343df0 --- /dev/null +++ b/src/Luban.Job.Common/Source/Defs/DefTypeBase.cs @@ -0,0 +1,47 @@ +using Luban.Common.Utils; +using Luban.Server.Common; + +namespace Luban.Job.Common.Defs +{ + public abstract class DefTypeBase + { + public DefAssemblyBase AssemblyBase { get; set; } + + public int Id { get; protected set; } + + public string TopModule => AssemblyBase.TopModule; + + public RemoteAgent Agent => AssemblyBase.Agent; + + public string Name { get; set; } + + public string Namespace { get; set; } + + public string FullName => TypeUtil.MakeFullName(Namespace, Name); + + public string NamespaceWithTopModule => TypeUtil.MakeNamespace(AssemblyBase.TopModule, Namespace); + + public string FullNameWithTopModule => TypeUtil.MakeFullName(AssemblyBase.TopModule, FullName); + + public string GoFullName => TypeUtil.MakeGoFullName(Namespace, Name); + + public string GoPkgName => TypeUtil.MakeGoPkgName(Namespace); + + public string CppNamespaceBegin => TypeUtil.MakeCppNamespaceBegin(Namespace); + + public string CppNamespaceEnd => TypeUtil.MakeCppNamespaceEnd(Namespace); + + public string CppFullNameWithTopModule => TypeUtil.MakeCppFullName(AssemblyBase.TopModule, FullName); + + + public string CppFullName => TypeUtil.MakeCppFullName(Namespace, Name); + + public string PyFullName => TypeUtil.MakePyFullName(Namespace, Name); + + public virtual void PreCompile() { } + + public abstract void Compile(); + + public virtual void PostCompile() { } + } +} diff --git a/src/Luban.Job.Common/Source/Defs/TTypeTemplateCommonExtends.cs b/src/Luban.Job.Common/Source/Defs/TTypeTemplateCommonExtends.cs new file mode 100644 index 0000000..a07d77c --- /dev/null +++ b/src/Luban.Job.Common/Source/Defs/TTypeTemplateCommonExtends.cs @@ -0,0 +1,89 @@ +using Luban.Job.Common.Types; +using Luban.Job.Common.TypeVisitors; +using Scriban.Runtime; + +namespace Luban.Job.Common.Defs +{ + public class TTypeTemplateCommonExtends : ScriptObject + { + public static string CsDefineType(TType type) + { + return type.Apply(CsDefineTypeName.Ins); + } + + public static string CsToString(string filedName, TType type) + { + return $"{filedName}"; + } + + public static string CsConstValue(TType type, string value) + { + return type.Apply(CsConstValueVisitor.Ins, value); + } + + public static string JavaDefineType(TType type) + { + return type.Apply(JavaDefineTypeName.Ins); + } + + public static string JavaBoxDefineType(TType type) + { + return type.Apply(JavaBoxDefineTypeName.Ins); + } + + public static string JavaToString(string filedName, TType type) + { + return $"{filedName}"; + } + + public static string JavaConstValue(TType type, string value) + { + return type.Apply(CsConstValueVisitor.Ins, value); + } + + public static string CppDefineType(TType type) + { + return type.Apply(CppDefineTypeName.Ins); + } + + public static string CppConstValue(TType type, string value) + { + return type.Apply(CsConstValueVisitor.Ins, value); + } + + public static string LuaConstValue(TType type, string value) + { + return type.Apply(LuaConstValueVisitor.Ins, value); + } + + public static string GoConstValue(TType type, string value) + { + return type.Apply(LuaConstValueVisitor.Ins, value); + } + + public static string TsDefineType(TType type) + { + return type.Apply(TsDefineTypeName.Ins); + } + + public static string TsToString(string filedName, TType type) + { + return $"{filedName}"; + } + + public static string TsConstValue(TType type, string value) + { + return type.Apply(LuaConstValueVisitor.Ins, value); + } + + public static string PyDefineType(TType type) + { + return type.Apply(PyDefineTypeName.Ins); + } + + public static string PyConstValue(TType type, string value) + { + return type.Apply(LuaConstValueVisitor.Ins, value); + } + } +} diff --git a/src/Luban.Job.Common/Source/ELanguage.cs b/src/Luban.Job.Common/Source/ELanguage.cs new file mode 100644 index 0000000..46a2dd0 --- /dev/null +++ b/src/Luban.Job.Common/Source/ELanguage.cs @@ -0,0 +1,14 @@ +namespace Luban.Job.Common +{ + public enum ELanguage + { + CS, + JAVA, + GO, + CPP, + LUA, + JS, + TYPESCRIPT, + PYTHON, + } +} diff --git a/src/Luban.Job.Common/Source/RawDefs/Bean.cs b/src/Luban.Job.Common/Source/RawDefs/Bean.cs new file mode 100644 index 0000000..7ec84e0 --- /dev/null +++ b/src/Luban.Job.Common/Source/RawDefs/Bean.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; + +namespace Luban.Job.Common.RawDefs +{ + public class Bean + { + public string Namespace { get; set; } + + public string Name { get; set; } + + public string FullName => Namespace.Length > 0 ? Namespace + "." + Name : Name; + + public string Parent { get; set; } + + public bool IsValueType { get; set; } + + public int TypeId { get; set; } + + public bool IsSerializeCompatible { get; set; } + + public List<Field> Fields { get; set; } = new List<Field>(); + } +} diff --git a/src/Luban.Job.Common/Source/RawDefs/Const.cs b/src/Luban.Job.Common/Source/RawDefs/Const.cs new file mode 100644 index 0000000..bdc9dfb --- /dev/null +++ b/src/Luban.Job.Common/Source/RawDefs/Const.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; + +namespace Luban.Job.Common.RawDefs +{ + public class ConstItem + { + public string Name { get; set; } + + public string Type { get; set; } + + public string Value { get; set; } + } + + public class Const + { + + public string Namespace { get; set; } + + public string Name { get; set; } + + public List<ConstItem> Items { get; set; } = new List<ConstItem>(); + } +} diff --git a/src/Luban.Job.Common/Source/RawDefs/Field.cs b/src/Luban.Job.Common/Source/RawDefs/Field.cs new file mode 100644 index 0000000..17c5322 --- /dev/null +++ b/src/Luban.Job.Common/Source/RawDefs/Field.cs @@ -0,0 +1,12 @@ +namespace Luban.Job.Common.RawDefs +{ + + public class Field + { + public int Id { get; set; } + + public string Name { get; set; } + + public string Type { get; set; } + } +} diff --git a/src/Luban.Job.Common/Source/RawDefs/PEnum.cs b/src/Luban.Job.Common/Source/RawDefs/PEnum.cs new file mode 100644 index 0000000..5e2471a --- /dev/null +++ b/src/Luban.Job.Common/Source/RawDefs/PEnum.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; + +namespace Luban.Job.Common.RawDefs +{ + public class EnumItem + { + public string Name { get; set; } + + public string Alias { get; set; } + + public string Value { get; set; } + } + + public class PEnum + { + + public string Namespace { get; set; } + + public string Name { get; set; } + + public bool IsFlags { get; set; } + + public bool IsUniqueItemId { get; set; } + + public List<EnumItem> Items { get; set; } = new List<EnumItem>(); + } +} diff --git a/src/Luban.Job.Common/Source/TypeVisitors/AllFalseVisitor.cs b/src/Luban.Job.Common/Source/TypeVisitors/AllFalseVisitor.cs new file mode 100644 index 0000000..989e137 --- /dev/null +++ b/src/Luban.Job.Common/Source/TypeVisitors/AllFalseVisitor.cs @@ -0,0 +1,12 @@ +using Luban.Job.Common.Types; + +namespace Luban.Job.Common.TypeVisitors +{ + public abstract class AllFalseVisitor : DecoratorFuncVisitor<bool> + { + public override bool DoAccept(TType type) + { + return false; + } + } +} diff --git a/src/Luban.Job.Common/Source/TypeVisitors/AllTrueVisitor.cs b/src/Luban.Job.Common/Source/TypeVisitors/AllTrueVisitor.cs new file mode 100644 index 0000000..25d1d66 --- /dev/null +++ b/src/Luban.Job.Common/Source/TypeVisitors/AllTrueVisitor.cs @@ -0,0 +1,12 @@ +using Luban.Job.Common.Types; + +namespace Luban.Job.Common.TypeVisitors +{ + public abstract class AllTrueVisitor : DecoratorFuncVisitor<bool> + { + public override bool DoAccept(TType type) + { + return true; + } + } +} diff --git a/src/Luban.Job.Common/Source/TypeVisitors/CppDefineTypeName.cs b/src/Luban.Job.Common/Source/TypeVisitors/CppDefineTypeName.cs new file mode 100644 index 0000000..dcfd9a3 --- /dev/null +++ b/src/Luban.Job.Common/Source/TypeVisitors/CppDefineTypeName.cs @@ -0,0 +1,20 @@ +using Luban.Job.Common.Types; + +namespace Luban.Job.Common.TypeVisitors +{ + public class CppDefineTypeName : DecoratorFuncVisitor<string> + { + public static CppDefineTypeName Ins { get; } = new CppDefineTypeName(); + + public override string DoAccept(TType type) + { + //return type.IsNullable ? $"std::optional<{type.Apply(CppUnderingDefineTypeName.Ins)}>" : type.Apply(CppUnderingDefineTypeName.Ins); + return type.Apply(CppUnderingDefineTypeName.Ins); + } + + public override string Accept(TBean type) + { + return type.Apply(CppUnderingDefineTypeName.Ins); + } + } +} diff --git a/src/Luban.Job.Common/Source/TypeVisitors/CppUnderingDefineTypeName.cs b/src/Luban.Job.Common/Source/TypeVisitors/CppUnderingDefineTypeName.cs new file mode 100644 index 0000000..4899bac --- /dev/null +++ b/src/Luban.Job.Common/Source/TypeVisitors/CppUnderingDefineTypeName.cs @@ -0,0 +1,124 @@ +using Luban.Job.Common.Types; + +namespace Luban.Job.Common.TypeVisitors +{ + public class CppUnderingDefineTypeName : ITypeFuncVisitor<string> + { + public static CppUnderingDefineTypeName Ins { get; } = new CppUnderingDefineTypeName(); + + public string Accept(TBool type) + { + return "bool"; + } + + public string Accept(TByte type) + { + return "uint8_t"; + } + + public string Accept(TShort type) + { + return "int16_t"; + } + + public string Accept(TFshort type) + { + return "int16_t"; + } + + public string Accept(TInt type) + { + return "int32_t"; + } + + public string Accept(TFint type) + { + return "int32_t"; + } + + public string Accept(TLong type) + { + return "int64_t"; + } + + public string Accept(TFlong type) + { + return "int64_t"; + } + + public string Accept(TFloat type) + { + return "float"; + } + + public string Accept(TDouble type) + { + return "double"; + } + + public string Accept(TEnum type) + { + return type.DefineEnum.CppFullName; + } + + public string Accept(TString type) + { + return "bright::String"; + } + + public string Accept(TBytes type) + { + return "bright::Bytes"; + } + + public string Accept(TText type) + { + return "bright::String"; + } + + public string Accept(TBean type) + { + return type.Bean.CppFullName + "*"; + } + + public string Accept(TArray type) + { + return $"std::vector<{type.ElementType.Apply(this)}>"; + } + + public string Accept(TList type) + { + return $"std::vector<{type.ElementType.Apply(this)}>"; + } + + public string Accept(TSet type) + { + return $"std::unordered_set<{type.ElementType.Apply(this)}>"; + } + + public string Accept(TMap type) + { + return $"std::unordered_map<{type.KeyType.Apply(this)}, {type.ValueType.Apply(this)}>"; + } + + public string Accept(TVector2 type) + { + return "bright::math::Vector2"; + } + + public string Accept(TVector3 type) + { + return "bright::math::Vector3"; + } + + public string Accept(TVector4 type) + { + return "bright::math::Vector4"; + } + + public string Accept(TDateTime type) + { + return "int32_t"; + } + } +} diff --git a/src/Luban.Job.Common/Source/TypeVisitors/CsConstValueVisitor.cs b/src/Luban.Job.Common/Source/TypeVisitors/CsConstValueVisitor.cs new file mode 100644 index 0000000..3a952b6 --- /dev/null +++ b/src/Luban.Job.Common/Source/TypeVisitors/CsConstValueVisitor.cs @@ -0,0 +1,125 @@ +using Luban.Job.Common.Types; +using System; + +namespace Luban.Job.Common.TypeVisitors +{ + public class CsConstValueVisitor : ITypeFuncVisitor<string, string> + { + public static CsConstValueVisitor Ins { get; } = new CsConstValueVisitor(); + + public string Accept(TBool type, string x) + { + return x.ToLower(); + } + + public string Accept(TByte type, string x) + { + return x; + } + + public string Accept(TShort type, string x) + { + return x; + } + + public string Accept(TFshort type, string x) + { + return x; + } + + public string Accept(TInt type, string x) + { + return x; + } + + public string Accept(TFint type, string x) + { + return x; + } + + public virtual string Accept(TLong type, string x) + { + return x + "L"; + } + + public virtual string Accept(TFlong type, string x) + { + return x + "L"; + } + + public virtual string Accept(TFloat type, string x) + { + return x + "f"; + } + + public string Accept(TDouble type, string x) + { + return x; + } + + public string Accept(TEnum type, string x) + { + return x; + } + + public string Accept(TString type, string x) + { + return "\"" + x + "\""; + } + + public string Accept(TBytes type, string x) + { + throw new NotImplementedException(); + } + + public string Accept(TText type, string x) + { + return "\"" + x + "\""; + } + + public string Accept(TBean type, string x) + { + throw new NotImplementedException(); + } + + public string Accept(TArray type, string x) + { + throw new NotImplementedException(); + } + + public string Accept(TList type, string x) + { + throw new NotImplementedException(); + } + + public string Accept(TSet type, string x) + { + throw new NotImplementedException(); + } + + public string Accept(TMap type, string x) + { + throw new NotImplementedException(); + } + + public string Accept(TVector2 type, string x) + { + throw new NotImplementedException(); + } + + public string Accept(TVector3 type, string x) + { + throw new NotImplementedException(); + } + + public string Accept(TVector4 type, string x) + { + throw new NotImplementedException(); + } + + public string Accept(TDateTime type, string x) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Luban.Job.Common/Source/TypeVisitors/CsDefineTypeName.cs b/src/Luban.Job.Common/Source/TypeVisitors/CsDefineTypeName.cs new file mode 100644 index 0000000..8d443d6 --- /dev/null +++ b/src/Luban.Job.Common/Source/TypeVisitors/CsDefineTypeName.cs @@ -0,0 +1,24 @@ +using Luban.Job.Common.Types; + +namespace Luban.Job.Common.TypeVisitors +{ + public class CsDefineTypeName : DecoratorFuncVisitor<string> + { + public static CsDefineTypeName Ins { get; } = new CsDefineTypeName(); + + public override string DoAccept(TType type) + { + return type.IsNullable ? (type.Apply(CsUnderingDefineTypeName.Ins) + "?") : type.Apply(CsUnderingDefineTypeName.Ins); + } + + public override string Accept(TString type) + { + return type.Apply(CsUnderingDefineTypeName.Ins); + } + + public override string Accept(TBean type) + { + return type.Apply(CsUnderingDefineTypeName.Ins); + } + } +} diff --git a/src/Luban.Job.Common/Source/TypeVisitors/CsUnderingDefineTypeName.cs b/src/Luban.Job.Common/Source/TypeVisitors/CsUnderingDefineTypeName.cs new file mode 100644 index 0000000..e8c18ea --- /dev/null +++ b/src/Luban.Job.Common/Source/TypeVisitors/CsUnderingDefineTypeName.cs @@ -0,0 +1,124 @@ +using Luban.Job.Common.Types; + +namespace Luban.Job.Common.TypeVisitors +{ + public class CsUnderingDefineTypeName : ITypeFuncVisitor<string> + { + public static CsUnderingDefineTypeName Ins { get; } = new CsUnderingDefineTypeName(); + + public string Accept(TBool type) + { + return "bool"; + } + + public string Accept(TByte type) + { + return "byte"; + } + + public string Accept(TShort type) + { + return "short"; + } + + public string Accept(TFshort type) + { + return "short"; + } + + public string Accept(TInt type) + { + return "int"; + } + + public string Accept(TFint type) + { + return "int"; + } + + public string Accept(TLong type) + { + return "long"; + } + + public string Accept(TFlong type) + { + return "long"; + } + + public string Accept(TFloat type) + { + return "float"; + } + + public string Accept(TDouble type) + { + return "double"; + } + + public string Accept(TEnum type) + { + return type.DefineEnum.FullName; + } + + public string Accept(TString type) + { + return "string"; + } + + public string Accept(TBytes type) + { + return "byte[]"; + } + + public string Accept(TText type) + { + return "string"; + } + + public string Accept(TBean type) + { + return type.Bean.FullName; + } + + public string Accept(TArray type) + { + return $"{type.ElementType.Apply(this)}[]"; + } + + public string Accept(TList type) + { + return $"List<{type.ElementType.Apply(this)}>"; + } + + public string Accept(TSet type) + { + return $"HashSet<{type.ElementType.Apply(this)}>"; + } + + public string Accept(TMap type) + { + return $"Dictionary<{type.KeyType.Apply(this)}, {type.ValueType.Apply(this)}>"; + } + + public string Accept(TVector2 type) + { + return "System.Numerics.Vector2"; + } + + public string Accept(TVector3 type) + { + return "System.Numerics.Vector3"; + } + + public string Accept(TVector4 type) + { + return "System.Numerics.Vector4"; + } + + public string Accept(TDateTime type) + { + return "int"; + } + } +} diff --git a/src/Luban.Job.Common/Source/TypeVisitors/DecoratorActionVisitor.cs b/src/Luban.Job.Common/Source/TypeVisitors/DecoratorActionVisitor.cs new file mode 100644 index 0000000..fc67d1b --- /dev/null +++ b/src/Luban.Job.Common/Source/TypeVisitors/DecoratorActionVisitor.cs @@ -0,0 +1,249 @@ +using Luban.Job.Common.Types; + +namespace Luban.Job.Common.TypeVisitors +{ + public abstract class DecoratorActionVisitor<T> : ITypeActionVisitor<T> + { + public abstract void DoAccept(TType type, T x); + + public virtual void Accept(TBool type, T x) + { + DoAccept(type, x); + } + + public virtual void Accept(TByte type, T x) + { + DoAccept(type, x); + } + + public virtual void Accept(TShort type, T x) + { + DoAccept(type, x); + } + + public virtual void Accept(TFshort type, T x) + { + DoAccept(type, x); + } + + public virtual void Accept(TInt type, T x) + { + DoAccept(type, x); + } + + public virtual void Accept(TFint type, T x) + { + DoAccept(type, x); + } + + public virtual void Accept(TLong type, T x) + { + DoAccept(type, x); + } + + public virtual void Accept(TFlong type, T x) + { + DoAccept(type, x); + } + + public virtual void Accept(TFloat type, T x) + { + DoAccept(type, x); + } + + public virtual void Accept(TDouble type, T x) + { + DoAccept(type, x); + } + + public virtual void Accept(TEnum type, T x) + { + DoAccept(type, x); + } + + public virtual void Accept(TString type, T x) + { + DoAccept(type, x); + } + + public virtual void Accept(TBytes type, T x) + { + DoAccept(type, x); + } + + public virtual void Accept(TText type, T x) + { + DoAccept(type, x); + } + + public virtual void Accept(TBean type, T x) + { + DoAccept(type, x); + } + + public virtual void Accept(TArray type, T x) + { + DoAccept(type, x); + } + + public virtual void Accept(TList type, T x) + { + DoAccept(type, x); + } + + public virtual void Accept(TSet type, T x) + { + DoAccept(type, x); + } + + public virtual void Accept(TMap type, T x) + { + DoAccept(type, x); + } + + public virtual void Accept(TVector2 type, T x) + { + DoAccept(type, x); + } + + public virtual void Accept(TVector3 type, T x) + { + DoAccept(type, x); + } + + public virtual void Accept(TVector4 type, T x) + { + DoAccept(type, x); + } + + public virtual void Accept(TDateTime type, T x) + { + DoAccept(type, x); + } + + } + + public abstract class DecoratorActionVisitor<T1, T2> : ITypeActionVisitor<T1, T2> + { + + public abstract void DoAccept(TType type, T1 x, T2 y); + + public virtual void Accept(TBool type, T1 x, T2 y) + { + DoAccept(type, x, y); + } + + public virtual void Accept(TByte type, T1 x, T2 y) + { + DoAccept(type, x, y); + } + + public virtual void Accept(TShort type, T1 x, T2 y) + { + DoAccept(type, x, y); + } + + public virtual void Accept(TFshort type, T1 x, T2 y) + { + DoAccept(type, x, y); + } + + public virtual void Accept(TInt type, T1 x, T2 y) + { + DoAccept(type, x, y); + } + + public virtual void Accept(TFint type, T1 x, T2 y) + { + DoAccept(type, x, y); + } + + public virtual void Accept(TLong type, T1 x, T2 y) + { + DoAccept(type, x, y); + } + + public virtual void Accept(TFlong type, T1 x, T2 y) + { + DoAccept(type, x, y); + } + + public virtual void Accept(TFloat type, T1 x, T2 y) + { + DoAccept(type, x, y); + } + + public virtual void Accept(TDouble type, T1 x, T2 y) + { + DoAccept(type, x, y); + } + + public virtual void Accept(TEnum type, T1 x, T2 y) + { + DoAccept(type, x, y); + } + + public virtual void Accept(TString type, T1 x, T2 y) + { + DoAccept(type, x, y); + } + + public virtual void Accept(TBytes type, T1 x, T2 y) + { + DoAccept(type, x, y); + } + + public virtual void Accept(TText type, T1 x, T2 y) + { + DoAccept(type, x, y); + } + + public virtual void Accept(TBean type, T1 x, T2 y) + { + DoAccept(type, x, y); + } + + public virtual void Accept(TArray type, T1 x, T2 y) + { + DoAccept(type, x, y); + } + + public virtual void Accept(TList type, T1 x, T2 y) + { + DoAccept(type, x, y); + } + + public virtual void Accept(TSet type, T1 x, T2 y) + { + DoAccept(type, x, y); + } + + public virtual void Accept(TMap type, T1 x, T2 y) + { + DoAccept(type, x, y); + } + + public virtual void Accept(TVector2 type, T1 x, T2 y) + { + DoAccept(type, x, y); + } + + public virtual void Accept(TVector3 type, T1 x, T2 y) + { + DoAccept(type, x, y); + } + + public virtual void Accept(TVector4 type, T1 x, T2 y) + { + DoAccept(type, x, y); + } + + public virtual void Accept(TDateTime type, T1 x, T2 y) + { + DoAccept(type, x, y); + } + + + + } +} diff --git a/src/Luban.Job.Common/Source/TypeVisitors/DecoratorFuncVisitor.cs b/src/Luban.Job.Common/Source/TypeVisitors/DecoratorFuncVisitor.cs new file mode 100644 index 0000000..91ec0ac --- /dev/null +++ b/src/Luban.Job.Common/Source/TypeVisitors/DecoratorFuncVisitor.cs @@ -0,0 +1,486 @@ +using Luban.Job.Common.Types; + +namespace Luban.Job.Common.TypeVisitors +{ + public abstract class DecoratorFuncVisitor<TR> : ITypeFuncVisitor<TR> + { + public abstract TR DoAccept(TType type); + + public virtual TR Accept(TBool type) + { + return DoAccept(type); + } + + public virtual TR Accept(TByte type) + { + return DoAccept(type); + } + + public virtual TR Accept(TShort type) + { + return DoAccept(type); + } + + public virtual TR Accept(TFshort type) + { + return DoAccept(type); + } + + public virtual TR Accept(TInt type) + { + return DoAccept(type); + } + + public virtual TR Accept(TFint type) + { + return DoAccept(type); + } + + public virtual TR Accept(TLong type) + { + return DoAccept(type); + } + + public virtual TR Accept(TFlong type) + { + return DoAccept(type); + } + + public virtual TR Accept(TFloat type) + { + return DoAccept(type); + } + + public virtual TR Accept(TDouble type) + { + return DoAccept(type); + } + + public virtual TR Accept(TEnum type) + { + return DoAccept(type); + } + + public virtual TR Accept(TString type) + { + return DoAccept(type); + } + + public virtual TR Accept(TBytes type) + { + return DoAccept(type); + } + + public virtual TR Accept(TText type) + { + return DoAccept(type); + } + + public virtual TR Accept(TBean type) + { + return DoAccept(type); + } + + public virtual TR Accept(TArray type) + { + return DoAccept(type); + } + + public virtual TR Accept(TList type) + { + return DoAccept(type); + } + + public virtual TR Accept(TSet type) + { + return DoAccept(type); + } + + public virtual TR Accept(TMap type) + { + return DoAccept(type); + } + + public virtual TR Accept(TVector2 type) + { + return DoAccept(type); + } + + public virtual TR Accept(TVector3 type) + { + return DoAccept(type); + } + + public virtual TR Accept(TVector4 type) + { + return DoAccept(type); + } + + public virtual TR Accept(TDateTime type) + { + return DoAccept(type); + } + } + + public abstract class DecoratorFuncVisitor<T1, TR> : ITypeFuncVisitor<T1, TR> + { + public abstract TR DoAccept(TType tpye, T1 x); + + public virtual TR Accept(TBool type, T1 x) + { + return DoAccept(type, x); + } + + public virtual TR Accept(TByte type, T1 x) + { + return DoAccept(type, x); + } + + public virtual TR Accept(TShort type, T1 x) + { + return DoAccept(type, x); + } + + public virtual TR Accept(TFshort type, T1 x) + { + return DoAccept(type, x); + } + + public virtual TR Accept(TInt type, T1 x) + { + return DoAccept(type, x); + } + + public virtual TR Accept(TFint type, T1 x) + { + return DoAccept(type, x); + } + + public virtual TR Accept(TLong type, T1 x) + { + return DoAccept(type, x); + } + + public virtual TR Accept(TFlong type, T1 x) + { + return DoAccept(type, x); + } + + public virtual TR Accept(TFloat type, T1 x) + { + return DoAccept(type, x); + } + + public virtual TR Accept(TDouble type, T1 x) + { + return DoAccept(type, x); + } + + public virtual TR Accept(TEnum type, T1 x) + { + return DoAccept(type, x); + } + + public virtual TR Accept(TString type, T1 x) + { + return DoAccept(type, x); + } + + public virtual TR Accept(TBytes type, T1 x) + { + return DoAccept(type, x); + } + + public virtual TR Accept(TText type, T1 x) + { + return DoAccept(type, x); + } + + public virtual TR Accept(TBean type, T1 x) + { + return DoAccept(type, x); + } + + public virtual TR Accept(TArray type, T1 x) + { + return DoAccept(type, x); + } + + public virtual TR Accept(TList type, T1 x) + { + return DoAccept(type, x); + } + + public virtual TR Accept(TSet type, T1 x) + { + return DoAccept(type, x); + } + + public virtual TR Accept(TMap type, T1 x) + { + return DoAccept(type, x); + } + + public virtual TR Accept(TVector2 type, T1 x) + { + return DoAccept(type, x); + } + + public virtual TR Accept(TVector3 type, T1 x) + { + return DoAccept(type, x); + } + + public virtual TR Accept(TVector4 type, T1 x) + { + return DoAccept(type, x); + } + + public virtual TR Accept(TDateTime type, T1 x) + { + return DoAccept(type, x); + } + } + + public abstract class DecoratorFuncVisitor<T1, T2, TR> : ITypeFuncVisitor<T1, T2, TR> + { + public abstract TR DoAccept(TType tpye, T1 x, T2 y); + + public virtual TR Accept(TBool type, T1 x, T2 y) + { + return DoAccept(type, x, y); + } + + public virtual TR Accept(TByte type, T1 x, T2 y) + { + return DoAccept(type, x, y); + } + + public virtual TR Accept(TShort type, T1 x, T2 y) + { + return DoAccept(type, x, y); + } + + public virtual TR Accept(TFshort type, T1 x, T2 y) + { + return DoAccept(type, x, y); + } + + public virtual TR Accept(TInt type, T1 x, T2 y) + { + return DoAccept(type, x, y); + } + + public virtual TR Accept(TFint type, T1 x, T2 y) + { + return DoAccept(type, x, y); + } + + public virtual TR Accept(TLong type, T1 x, T2 y) + { + return DoAccept(type, x, y); + } + + public virtual TR Accept(TFlong type, T1 x, T2 y) + { + return DoAccept(type, x, y); + } + + public virtual TR Accept(TFloat type, T1 x, T2 y) + { + return DoAccept(type, x, y); + } + + public virtual TR Accept(TDouble type, T1 x, T2 y) + { + return DoAccept(type, x, y); + } + + public virtual TR Accept(TEnum type, T1 x, T2 y) + { + return DoAccept(type, x, y); + } + + public virtual TR Accept(TString type, T1 x, T2 y) + { + return DoAccept(type, x, y); + } + + public virtual TR Accept(TBytes type, T1 x, T2 y) + { + return DoAccept(type, x, y); + } + + public virtual TR Accept(TText type, T1 x, T2 y) + { + return DoAccept(type, x, y); + } + + public virtual TR Accept(TBean type, T1 x, T2 y) + { + return DoAccept(type, x, y); + } + + public virtual TR Accept(TArray type, T1 x, T2 y) + { + return DoAccept(type, x, y); + } + + public virtual TR Accept(TList type, T1 x, T2 y) + { + return DoAccept(type, x, y); + } + + public virtual TR Accept(TSet type, T1 x, T2 y) + { + return DoAccept(type, x, y); + } + + public virtual TR Accept(TMap type, T1 x, T2 y) + { + return DoAccept(type, x, y); + } + + public virtual TR Accept(TVector2 type, T1 x, T2 y) + { + return DoAccept(type, x, y); + } + + public virtual TR Accept(TVector3 type, T1 x, T2 y) + { + return DoAccept(type, x, y); + } + + public virtual TR Accept(TVector4 type, T1 x, T2 y) + { + return DoAccept(type, x, y); + } + + public virtual TR Accept(TDateTime type, T1 x, T2 y) + { + return DoAccept(type, x, y); + } + } + + public abstract class DecoratorFuncVisitor<T1, T2, T3, TR> : ITypeFuncVisitor<T1, T2, T3, TR> + { + + public abstract TR DoAccept(TType tpye, T1 x, T2 y, T3 z); + + public virtual TR Accept(TBool type, T1 x, T2 y, T3 z) + { + return DoAccept(type, x, y, z); + } + + public virtual TR Accept(TByte type, T1 x, T2 y, T3 z) + { + return DoAccept(type, x, y, z); + } + + public virtual TR Accept(TShort type, T1 x, T2 y, T3 z) + { + return DoAccept(type, x, y, z); + } + + public virtual TR Accept(TFshort type, T1 x, T2 y, T3 z) + { + return DoAccept(type, x, y, z); + } + + public virtual TR Accept(TInt type, T1 x, T2 y, T3 z) + { + return DoAccept(type, x, y, z); + } + + public virtual TR Accept(TFint type, T1 x, T2 y, T3 z) + { + return DoAccept(type, x, y, z); + } + + public virtual TR Accept(TLong type, T1 x, T2 y, T3 z) + { + return DoAccept(type, x, y, z); + } + + public virtual TR Accept(TFlong type, T1 x, T2 y, T3 z) + { + return DoAccept(type, x, y, z); + } + + public virtual TR Accept(TFloat type, T1 x, T2 y, T3 z) + { + return DoAccept(type, x, y, z); + } + + public virtual TR Accept(TDouble type, T1 x, T2 y, T3 z) + { + return DoAccept(type, x, y, z); + } + + public virtual TR Accept(TEnum type, T1 x, T2 y, T3 z) + { + return DoAccept(type, x, y, z); + } + + public virtual TR Accept(TString type, T1 x, T2 y, T3 z) + { + return DoAccept(type, x, y, z); + } + + public virtual TR Accept(TBytes type, T1 x, T2 y, T3 z) + { + return DoAccept(type, x, y, z); + } + + public virtual TR Accept(TText type, T1 x, T2 y, T3 z) + { + return DoAccept(type, x, y, z); + } + + public virtual TR Accept(TBean type, T1 x, T2 y, T3 z) + { + return DoAccept(type, x, y, z); + } + + public virtual TR Accept(TArray type, T1 x, T2 y, T3 z) + { + return DoAccept(type, x, y, z); + } + + public virtual TR Accept(TList type, T1 x, T2 y, T3 z) + { + return DoAccept(type, x, y, z); + } + + public virtual TR Accept(TSet type, T1 x, T2 y, T3 z) + { + return DoAccept(type, x, y, z); + } + + public virtual TR Accept(TMap type, T1 x, T2 y, T3 z) + { + return DoAccept(type, x, y, z); + } + + public virtual TR Accept(TVector2 type, T1 x, T2 y, T3 z) + { + return DoAccept(type, x, y, z); + } + + public virtual TR Accept(TVector3 type, T1 x, T2 y, T3 z) + { + return DoAccept(type, x, y, z); + } + + public virtual TR Accept(TVector4 type, T1 x, T2 y, T3 z) + { + return DoAccept(type, x, y, z); + } + + public virtual TR Accept(TDateTime type, T1 x, T2 y, T3 z) + { + return DoAccept(type, x, y, z); + } + + } +} diff --git a/src/Luban.Job.Common/Source/TypeVisitors/ITypeActionVisitor.cs b/src/Luban.Job.Common/Source/TypeVisitors/ITypeActionVisitor.cs new file mode 100644 index 0000000..c9967de --- /dev/null +++ b/src/Luban.Job.Common/Source/TypeVisitors/ITypeActionVisitor.cs @@ -0,0 +1,103 @@ +using Luban.Job.Common.Types; + +namespace Luban.Job.Common.TypeVisitors +{ + + public interface ITypeActionVisitor<T> + { + void Accept(TBool type, T x); + + void Accept(TByte type, T x); + + void Accept(TShort type, T x); + + void Accept(TFshort type, T x); + + void Accept(TInt type, T x); + + void Accept(TFint type, T x); + + void Accept(TLong type, T x); + + void Accept(TFlong type, T x); + + void Accept(TFloat type, T x); + + void Accept(TDouble type, T x); + + void Accept(TEnum type, T x); + + void Accept(TString type, T x); + + void Accept(TBytes type, T x); + + void Accept(TText type, T x); + + void Accept(TBean type, T x); + + void Accept(TArray type, T x); + + void Accept(TList type, T x); + + void Accept(TSet type, T x); + + void Accept(TMap type, T x); + + void Accept(TVector2 type, T x); + + void Accept(TVector3 type, T x); + + void Accept(TVector4 type, T x); + + void Accept(TDateTime type, T x); + } + + public interface ITypeActionVisitor<T1, T2> + { + void Accept(TBool type, T1 x, T2 y); + + void Accept(TByte type, T1 x, T2 y); + + void Accept(TShort type, T1 x, T2 y); + + void Accept(TFshort type, T1 x, T2 y); + + void Accept(TInt type, T1 x, T2 y); + + void Accept(TFint type, T1 x, T2 y); + + void Accept(TLong type, T1 x, T2 y); + + void Accept(TFlong type, T1 x, T2 y); + + void Accept(TFloat type, T1 x, T2 y); + + void Accept(TDouble type, T1 x, T2 y); + + void Accept(TEnum type, T1 x, T2 y); + + void Accept(TString type, T1 x, T2 y); + + void Accept(TBytes type, T1 x, T2 y); + + void Accept(TText type, T1 x, T2 y); + + void Accept(TBean type, T1 x, T2 y); + + void Accept(TArray type, T1 x, T2 y); + + void Accept(TList type, T1 x, T2 y); + + void Accept(TSet type, T1 x, T2 y); + + void Accept(TMap type, T1 x, T2 y); + + void Accept(TVector2 type, T1 x, T2 y); + + void Accept(TVector3 type, T1 x, T2 y); + + void Accept(TVector4 type, T1 x, T2 y); + + void Accept(TDateTime type, T1 x, T2 y); + } +} diff --git a/src/Luban.Job.Common/Source/TypeVisitors/ITypeFuncVisitor.cs b/src/Luban.Job.Common/Source/TypeVisitors/ITypeFuncVisitor.cs new file mode 100644 index 0000000..1b755c1 --- /dev/null +++ b/src/Luban.Job.Common/Source/TypeVisitors/ITypeFuncVisitor.cs @@ -0,0 +1,200 @@ +using Luban.Job.Common.Types; + +namespace Luban.Job.Common.TypeVisitors +{ + public interface ITypeFuncVisitor<TR> + { + TR Accept(TBool type); + + TR Accept(TByte type); + + TR Accept(TShort type); + + TR Accept(TFshort type); + + TR Accept(TInt type); + + TR Accept(TFint type); + + TR Accept(TLong type); + + TR Accept(TFlong type); + + TR Accept(TFloat type); + + TR Accept(TDouble type); + + TR Accept(TEnum type); + + TR Accept(TString type); + + TR Accept(TBytes type); + + TR Accept(TText type); + + TR Accept(TBean type); + + TR Accept(TArray type); + + TR Accept(TList type); + + TR Accept(TSet type); + + TR Accept(TMap type); + + TR Accept(TVector2 type); + + TR Accept(TVector3 type); + + TR Accept(TVector4 type); + + TR Accept(TDateTime type); + } + + public interface ITypeFuncVisitor<T, TR> + { + TR Accept(TBool type, T x); + + TR Accept(TByte type, T x); + + TR Accept(TShort type, T x); + + TR Accept(TFshort type, T x); + + TR Accept(TInt type, T x); + + TR Accept(TFint type, T x); + + TR Accept(TLong type, T x); + + TR Accept(TFlong type, T x); + + TR Accept(TFloat type, T x); + + TR Accept(TDouble type, T x); + + TR Accept(TEnum type, T x); + + TR Accept(TString type, T x); + + TR Accept(TBytes type, T x); + + TR Accept(TText type, T x); + + TR Accept(TBean type, T x); + + TR Accept(TArray type, T x); + + TR Accept(TList type, T x); + + TR Accept(TSet type, T x); + + TR Accept(TMap type, T x); + + TR Accept(TVector2 type, T x); + + TR Accept(TVector3 type, T x); + + TR Accept(TVector4 type, T x); + + TR Accept(TDateTime type, T x); + } + + public interface ITypeFuncVisitor<T, T2, TR> + { + TR Accept(TBool type, T x, T2 y); + + TR Accept(TByte type, T x, T2 y); + + TR Accept(TShort type, T x, T2 y); + + TR Accept(TFshort type, T x, T2 y); + + TR Accept(TInt type, T x, T2 y); + + TR Accept(TFint type, T x, T2 y); + + TR Accept(TLong type, T x, T2 y); + + TR Accept(TFlong type, T x, T2 y); + + TR Accept(TFloat type, T x, T2 y); + + TR Accept(TDouble type, T x, T2 y); + + TR Accept(TEnum type, T x, T2 y); + + TR Accept(TString type, T x, T2 y); + + TR Accept(TBytes type, T x, T2 y); + + TR Accept(TText type, T x, T2 y); + + TR Accept(TBean type, T x, T2 y); + + TR Accept(TArray type, T x, T2 y); + + TR Accept(TList type, T x, T2 y); + + TR Accept(TSet type, T x, T2 y); + + TR Accept(TMap type, T x, T2 y); + + TR Accept(TVector2 type, T x, T2 y); + + TR Accept(TVector3 type, T x, T2 y); + + TR Accept(TVector4 type, T x, T2 y); + + TR Accept(TDateTime type, T x, T2 y); + } + + public interface ITypeFuncVisitor<T, T2, T3, TR> + { + TR Accept(TBool type, T x, T2 y, T3 z); + + TR Accept(TByte type, T x, T2 y, T3 z); + + TR Accept(TShort type, T x, T2 y, T3 z); + + TR Accept(TFshort type, T x, T2 y, T3 z); + + TR Accept(TInt type, T x, T2 y, T3 z); + + TR Accept(TFint type, T x, T2 y, T3 z); + + TR Accept(TLong type, T x, T2 y, T3 z); + + TR Accept(TFlong type, T x, T2 y, T3 z); + + TR Accept(TFloat type, T x, T2 y, T3 z); + + TR Accept(TDouble type, T x, T2 y, T3 z); + + TR Accept(TEnum type, T x, T2 y, T3 z); + + TR Accept(TString type, T x, T2 y, T3 z); + + TR Accept(TBytes type, T x, T2 y, T3 z); + + TR Accept(TText type, T x, T2 y, T3 z); + + TR Accept(TBean type, T x, T2 y, T3 z); + + TR Accept(TArray type, T x, T2 y, T3 z); + + TR Accept(TList type, T x, T2 y, T3 z); + + TR Accept(TSet type, T x, T2 y, T3 z); + + TR Accept(TMap type, T x, T2 y, T3 z); + + TR Accept(TVector2 type, T x, T2 y, T3 z); + + TR Accept(TVector3 type, T x, T2 y, T3 z); + + TR Accept(TVector4 type, T x, T2 y, T3 z); + + TR Accept(TDateTime type, T x, T2 y, T3 z); + } +} diff --git a/src/Luban.Job.Common/Source/TypeVisitors/IsCollectionType.cs b/src/Luban.Job.Common/Source/TypeVisitors/IsCollectionType.cs new file mode 100644 index 0000000..f879eea --- /dev/null +++ b/src/Luban.Job.Common/Source/TypeVisitors/IsCollectionType.cs @@ -0,0 +1,30 @@ +using Luban.Job.Common.Types; + +namespace Luban.Job.Common.TypeVisitors +{ + public class IsCollectionType : AllFalseVisitor + { + public static IsCollectionType Ins { get; } = new IsCollectionType(); + + + public override bool Accept(TArray type) + { + return true; + } + + public override bool Accept(TList type) + { + return true; + } + + public override bool Accept(TSet type) + { + return true; + } + + public override bool Accept(TMap type) + { + return true; + } + } +} diff --git a/src/Luban.Job.Common/Source/TypeVisitors/JavaBoxDefineTypeName.cs b/src/Luban.Job.Common/Source/TypeVisitors/JavaBoxDefineTypeName.cs new file mode 100644 index 0000000..0507bbc --- /dev/null +++ b/src/Luban.Job.Common/Source/TypeVisitors/JavaBoxDefineTypeName.cs @@ -0,0 +1,64 @@ +using Luban.Job.Common.Types; + +namespace Luban.Job.Common.TypeVisitors +{ + public class JavaBoxDefineTypeName : JavaDefineTypeName + { + public static new JavaBoxDefineTypeName Ins { get; } = new JavaBoxDefineTypeName(); + + public override string Accept(TBool type) + { + return "Boolean"; + } + + public override string Accept(TByte type) + { + return "Byte"; + } + + public override string Accept(TShort type) + { + return "Short"; + } + + public override string Accept(TFshort type) + { + return "Short"; + } + + public override string Accept(TInt type) + { + return "Integer"; + } + + public override string Accept(TFint type) + { + return "Integer"; + } + + public override string Accept(TLong type) + { + return "Long"; + } + + public override string Accept(TFlong type) + { + return "Long"; + } + + public override string Accept(TFloat type) + { + return "Float"; + } + + public override string Accept(TDouble type) + { + return "Double"; + } + + public override string Accept(TDateTime type) + { + return "Integer"; + } + } +} diff --git a/src/Luban.Job.Common/Source/TypeVisitors/JavaDefineTypeName.cs b/src/Luban.Job.Common/Source/TypeVisitors/JavaDefineTypeName.cs new file mode 100644 index 0000000..e84543a --- /dev/null +++ b/src/Luban.Job.Common/Source/TypeVisitors/JavaDefineTypeName.cs @@ -0,0 +1,124 @@ +using Luban.Job.Common.Types; + +namespace Luban.Job.Common.TypeVisitors +{ + public class JavaDefineTypeName : ITypeFuncVisitor<string> + { + public static JavaDefineTypeName Ins { get; } = new JavaDefineTypeName(); + + public virtual string Accept(TBool type) + { + return type.IsNullable ? "Boolean" : "boolean"; + } + + public virtual string Accept(TByte type) + { + return type.IsNullable ? "Byte" : "byte"; + } + + public virtual string Accept(TShort type) + { + return type.IsNullable ? "Short" : "short"; + } + + public virtual string Accept(TFshort type) + { + return type.IsNullable ? "Short" : "short"; + } + + public virtual string Accept(TInt type) + { + return type.IsNullable ? "Integer" : "int"; + } + + public virtual string Accept(TFint type) + { + return type.IsNullable ? "Integer" : "int"; + } + + public virtual string Accept(TLong type) + { + return type.IsNullable ? "Long" : "long"; + } + + public virtual string Accept(TFlong type) + { + return type.IsNullable ? "Long" : "long"; + } + + public virtual string Accept(TFloat type) + { + return type.IsNullable ? "Float" : "float"; + } + + public virtual string Accept(TDouble type) + { + return type.IsNullable ? "Double" : "double"; + } + + public string Accept(TEnum type) + { + return type.DefineEnum.FullNameWithTopModule; + } + + public string Accept(TString type) + { + return "String"; + } + + public string Accept(TBytes type) + { + return "byte[]"; + } + + public string Accept(TText type) + { + return "String"; + } + + public string Accept(TBean type) + { + return type.Bean.FullNameWithTopModule; + } + + public string Accept(TArray type) + { + return $"{type.ElementType.Apply(this)}[]"; + } + + public string Accept(TList type) + { + return $"java.util.ArrayList<{type.ElementType.Apply(JavaBoxDefineTypeName.Ins)}>"; + } + + public string Accept(TSet type) + { + return $"java.util.HashSet<{type.ElementType.Apply(JavaBoxDefineTypeName.Ins)}>"; + } + + public string Accept(TMap type) + { + return $"java.util.HashMap<{type.KeyType.Apply(JavaBoxDefineTypeName.Ins)}, {type.ValueType.Apply(JavaBoxDefineTypeName.Ins)}>"; + } + + public string Accept(TVector2 type) + { + return "bright.math.Vector2"; + } + + public string Accept(TVector3 type) + { + return "bright.math.Vector3"; + } + + public string Accept(TVector4 type) + { + return "bright.math.Vector4"; + } + + public virtual string Accept(TDateTime type) + { + return type.IsNullable ? "Integer" : "int"; + } + } +} diff --git a/src/Luban.Job.Common/Source/TypeVisitors/LuaCommentTypeVisitor.cs b/src/Luban.Job.Common/Source/TypeVisitors/LuaCommentTypeVisitor.cs new file mode 100644 index 0000000..2ccd825 --- /dev/null +++ b/src/Luban.Job.Common/Source/TypeVisitors/LuaCommentTypeVisitor.cs @@ -0,0 +1,124 @@ +using Luban.Job.Common.Types; + +namespace Luban.Job.Common.TypeVisitors +{ + public class LuaCommentTypeVisitor : ITypeFuncVisitor<string> + { + public static LuaCommentTypeVisitor Ins { get; } = new LuaCommentTypeVisitor(); + + public string Accept(TBool type) + { + return "bool"; + } + + public string Accept(TByte type) + { + return "byte"; + } + + public string Accept(TShort type) + { + return "short"; + } + + public string Accept(TFshort type) + { + return "short"; + } + + public string Accept(TInt type) + { + return "int"; + } + + public string Accept(TFint type) + { + return "int"; + } + + public string Accept(TLong type) + { + return "long"; + } + + public string Accept(TFlong type) + { + return "long"; + } + + public string Accept(TFloat type) + { + return "float"; + } + + public string Accept(TDouble type) + { + return "double"; + } + + public string Accept(TEnum type) + { + return type.DefineEnum.FullName; + } + + public string Accept(TString type) + { + return "string"; + } + + public string Accept(TBytes type) + { + return $"string"; + } + + public string Accept(TText type) + { + return "string"; + } + + public string Accept(TBean type) + { + return type.Bean.FullName; + } + + public string Accept(TArray type) + { + return $"{type.ElementType.Apply(this)}[]"; + } + + public string Accept(TList type) + { + return $"{type.ElementType.Apply(this)}[]"; + } + + public string Accept(TSet type) + { + return $"{type.ElementType.Apply(this)}[]"; + } + + public string Accept(TMap type) + { + return $"table<{type.KeyType.Apply(this)},{type.ValueType.Apply(this)}>"; + } + + public string Accept(TVector2 type) + { + return "vector2"; + } + + public string Accept(TVector3 type) + { + return "vector3"; + } + + public string Accept(TVector4 type) + { + return "vector4"; + } + + public string Accept(TDateTime type) + { + return "int"; + } + } +} diff --git a/src/Luban.Job.Common/Source/TypeVisitors/LuaConstValueVisitor.cs b/src/Luban.Job.Common/Source/TypeVisitors/LuaConstValueVisitor.cs new file mode 100644 index 0000000..3a0431a --- /dev/null +++ b/src/Luban.Job.Common/Source/TypeVisitors/LuaConstValueVisitor.cs @@ -0,0 +1,24 @@ +using Luban.Job.Common.Types; + +namespace Luban.Job.Common.TypeVisitors +{ + public class LuaConstValueVisitor : CsConstValueVisitor + { + public static new LuaConstValueVisitor Ins { get; } = new LuaConstValueVisitor(); + + public override string Accept(TLong type, string x) + { + return x; + } + + public override string Accept(TFlong type, string x) + { + return x; + } + + public override string Accept(TFloat type, string x) + { + return x; + } + } +} diff --git a/src/Luban.Job.Common/Source/TypeVisitors/LuaDeserializeMethodNameVisitor.cs b/src/Luban.Job.Common/Source/TypeVisitors/LuaDeserializeMethodNameVisitor.cs new file mode 100644 index 0000000..72ef28b --- /dev/null +++ b/src/Luban.Job.Common/Source/TypeVisitors/LuaDeserializeMethodNameVisitor.cs @@ -0,0 +1,124 @@ +using Luban.Job.Common.Types; + +namespace Luban.Job.Common.TypeVisitors +{ + public class LuaDeserializeMethodNameVisitor : ITypeFuncVisitor<string> + { + public static LuaDeserializeMethodNameVisitor Ins { get; } = new LuaDeserializeMethodNameVisitor(); + + public string Accept(TBool type) + { + return "readBool"; + } + + public string Accept(TByte type) + { + return "readByte"; + } + + public string Accept(TShort type) + { + return "readShort"; + } + + public string Accept(TFshort type) + { + return "readFshort"; + } + + public string Accept(TInt type) + { + return "readInt"; + } + + public string Accept(TFint type) + { + return "readFint"; + } + + public string Accept(TLong type) + { + return "readLong"; + } + + public string Accept(TFlong type) + { + return "readFlong"; + } + + public string Accept(TFloat type) + { + return "readFloat"; + } + + public string Accept(TDouble type) + { + return "readDouble"; + } + + public string Accept(TEnum type) + { + return "readInt"; + } + + public string Accept(TString type) + { + return "readString"; + } + + public string Accept(TBytes type) + { + return "readString"; + } + + public string Accept(TText type) + { + return "readString"; + } + + public string Accept(TBean type) + { + return $"beans['{type.Bean.FullName}']._deserialize"; + } + + public string Accept(TArray type) + { + return "readList"; + } + + public string Accept(TList type) + { + return "readList"; + } + + public string Accept(TSet type) + { + return "readSet"; + } + + public string Accept(TMap type) + { + return "readMap"; + } + + public string Accept(TVector2 type) + { + return "readVector2"; + } + + public string Accept(TVector3 type) + { + return "readVector3"; + } + + public string Accept(TVector4 type) + { + return "readVector4"; + } + + public string Accept(TDateTime type) + { + return "readInt"; + } + } +} diff --git a/src/Luban.Job.Common/Source/TypeVisitors/LuaUnderingDeserializeVisitor.cs b/src/Luban.Job.Common/Source/TypeVisitors/LuaUnderingDeserializeVisitor.cs new file mode 100644 index 0000000..1837b89 --- /dev/null +++ b/src/Luban.Job.Common/Source/TypeVisitors/LuaUnderingDeserializeVisitor.cs @@ -0,0 +1,34 @@ +using Luban.Job.Common.Types; + +namespace Luban.Job.Common.TypeVisitors +{ + public class LuaUnderingDeserializeVisitor : DecoratorFuncVisitor<string, string> + { + public static LuaUnderingDeserializeVisitor Ins { get; } = new LuaUnderingDeserializeVisitor(); + + public override string DoAccept(TType type, string x) + { + return $"{type.Apply(LuaDeserializeMethodNameVisitor.Ins)}({x})"; + } + + public override string Accept(TArray type, string x) + { + return $"readList({x}, {type.ElementType.Apply(LuaDeserializeMethodNameVisitor.Ins)})"; + } + + public override string Accept(TList type, string x) + { + return $"readList({x}, {type.ElementType.Apply(LuaDeserializeMethodNameVisitor.Ins)})"; + } + + public override string Accept(TSet type, string x) + { + return $"readSet({x}, {type.ElementType.Apply(LuaDeserializeMethodNameVisitor.Ins)})"; + } + + public override string Accept(TMap type, string x) + { + return $"readMap({x}, {type.KeyType.Apply(LuaDeserializeMethodNameVisitor.Ins)}, {type.ValueType.Apply(LuaDeserializeMethodNameVisitor.Ins)})"; + } + } +} diff --git a/src/Luban.Job.Common/Source/TypeVisitors/LuaValueOrDefaultVisitor.cs b/src/Luban.Job.Common/Source/TypeVisitors/LuaValueOrDefaultVisitor.cs new file mode 100644 index 0000000..145519b --- /dev/null +++ b/src/Luban.Job.Common/Source/TypeVisitors/LuaValueOrDefaultVisitor.cs @@ -0,0 +1,125 @@ +using Luban.Job.Common.Types; +using System; + +namespace Luban.Job.Common.TypeVisitors +{ + public class LuaValueOrDefaultVisitor : ITypeFuncVisitor<string, string> + { + public static LuaValueOrDefaultVisitor Ins { get; } = new LuaValueOrDefaultVisitor(); + + public string Accept(TBool type, string x) + { + return $"{x} == true"; + } + + public string Accept(TByte type, string x) + { + return $"{x} or 0"; + } + + public string Accept(TShort type, string x) + { + return $"{x} or 0"; + } + + public string Accept(TFshort type, string x) + { + return $"{x} or 0"; + } + + public string Accept(TInt type, string x) + { + return $"{x} or 0"; + } + + public string Accept(TFint type, string x) + { + return $"{x} or 0"; + } + + public string Accept(TLong type, string x) + { + return $"{x} or 0"; + } + + public string Accept(TFlong type, string x) + { + return $"{x} or 0"; + } + + public string Accept(TFloat type, string x) + { + return $"{x} or 0"; + } + + public string Accept(TDouble type, string x) + { + return $"{x} or 0"; + } + + public string Accept(TEnum type, string x) + { + return $"{x} or 0"; + } + + public string Accept(TString type, string x) + { + return $"{x} or \"\""; + } + + public string Accept(TBytes type, string x) + { + return $"{x} or \"\""; + } + + public string Accept(TText type, string x) + { + throw new NotImplementedException(); + } + + public string Accept(TBean type, string x) + { + return $"{x} or {{}}"; + } + + public string Accept(TArray type, string x) + { + return $"{x} or {{}}"; + } + + public string Accept(TList type, string x) + { + return $"{x} or {{}}"; + } + + public string Accept(TSet type, string x) + { + return $"{x} or {{}}"; + } + + public string Accept(TMap type, string x) + { + return $"{x} or {{}}"; + } + + public string Accept(TVector2 type, string x) + { + return $"{x} or default_vector2"; + } + + public string Accept(TVector3 type, string x) + { + return $"{x} or default_vector3"; + } + + public string Accept(TVector4 type, string x) + { + return $"{x} or default_vector4"; + } + + public string Accept(TDateTime type, string x) + { + return $"{x} or 0"; + } + } +} diff --git a/src/Luban.Job.Common/Source/TypeVisitors/NeedMarshalBoolPrefixVisitor.cs b/src/Luban.Job.Common/Source/TypeVisitors/NeedMarshalBoolPrefixVisitor.cs new file mode 100644 index 0000000..6a85626 --- /dev/null +++ b/src/Luban.Job.Common/Source/TypeVisitors/NeedMarshalBoolPrefixVisitor.cs @@ -0,0 +1,14 @@ +using Luban.Job.Common.Types; + +namespace Luban.Job.Common.TypeVisitors +{ + public class NeedMarshalBoolPrefixVisitor : DecoratorFuncVisitor<bool> + { + public static NeedMarshalBoolPrefixVisitor Ins { get; } = new NeedMarshalBoolPrefixVisitor(); + + public override bool DoAccept(TType type) + { + return type.IsNullable && !(type is TBean bean && bean.IsDynamic); + } + } +} diff --git a/src/Luban.Job.Common/Source/TypeVisitors/PyDefineTypeName.cs b/src/Luban.Job.Common/Source/TypeVisitors/PyDefineTypeName.cs new file mode 100644 index 0000000..eefc9f1 --- /dev/null +++ b/src/Luban.Job.Common/Source/TypeVisitors/PyDefineTypeName.cs @@ -0,0 +1,39 @@ +using Luban.Job.Common.Types; + +namespace Luban.Job.Common.TypeVisitors +{ + public class PyDefineTypeName : DecoratorFuncVisitor<string> + { + public static PyDefineTypeName Ins { get; } = new PyDefineTypeName(); + + public override string DoAccept(TType type) + { + throw new System.NotSupportedException(); + } + + public override string Accept(TEnum type) + { + return type.DefineEnum.PyFullName; + } + + public override string Accept(TBean type) + { + return type.Bean.PyFullName; + } + + public override string Accept(TVector2 type) + { + return "Vector2"; + } + + public override string Accept(TVector3 type) + { + return "Vector3"; + } + + public override string Accept(TVector4 type) + { + return "Vector4"; + } + } +} diff --git a/src/Luban.Job.Common/Source/TypeVisitors/TsDefineTypeName.cs b/src/Luban.Job.Common/Source/TypeVisitors/TsDefineTypeName.cs new file mode 100644 index 0000000..528c8e7 --- /dev/null +++ b/src/Luban.Job.Common/Source/TypeVisitors/TsDefineTypeName.cs @@ -0,0 +1,142 @@ +using Luban.Job.Common.Types; + +namespace Luban.Job.Common.TypeVisitors +{ + public class TsDefineTypeName : ITypeFuncVisitor<string> + { + public static TsDefineTypeName Ins { get; } = new TsDefineTypeName(); + + public string Accept(TBool type) + { + return "boolean"; + } + + public string Accept(TByte type) + { + return "number"; + } + + public string Accept(TShort type) + { + return "number"; + } + + public string Accept(TFshort type) + { + return "number"; + } + + public string Accept(TInt type) + { + return "number"; + } + + public string Accept(TFint type) + { + return "number"; + } + + public string Accept(TLong type) + { + return "number"; + } + + public string Accept(TFlong type) + { + return "number"; + } + + public string Accept(TFloat type) + { + return "number"; + } + + public string Accept(TDouble type) + { + return "number"; + } + + public string Accept(TEnum type) + { + return type.DefineEnum.FullName; + } + + public string Accept(TString type) + { + return "string"; + } + + public string Accept(TBytes type) + { + return "Uint8Array"; + } + + public string Accept(TText type) + { + return "string"; + } + + public string Accept(TBean type) + { + return type.Bean.FullName; + } + + + private string GetArrayType(TType elementType) + { + switch (elementType) + { + case TByte _: return "Uint8Array"; + case TShort _: + case TFshort _: return "Int16Array"; + case TInt _: + case TFint _: return "Int32Array"; + case TLong _: + case TFlong _: return "Int64Array"; + case TFloat _: return "Float32Array"; + case TDouble _: return "Float64Array"; + default: return $"{elementType.Apply(this)}[]"; + } + } + + public string Accept(TArray type) + { + return GetArrayType(type.ElementType); + } + + public string Accept(TList type) + { + return GetArrayType(type.ElementType); + } + + public string Accept(TSet type) + { + return $"Set<{type.ElementType.Apply(this)}>"; + } + + public string Accept(TMap type) + { + return $"Map<{type.KeyType.Apply(this)}, {type.ValueType.Apply(this)}>"; + } + + public string Accept(TVector2 type) + { + return "Vector2"; + } + + public string Accept(TVector3 type) + { + return "Vector3"; + } + + public string Accept(TVector4 type) + { + return "Vector4"; + } + + public string Accept(TDateTime type) + { + return "number"; + } + } +} diff --git a/src/Luban.Job.Common/Source/Types/TArray.cs b/src/Luban.Job.Common/Source/Types/TArray.cs new file mode 100644 index 0000000..a283854 --- /dev/null +++ b/src/Luban.Job.Common/Source/Types/TArray.cs @@ -0,0 +1,53 @@ +using Luban.Job.Common.TypeVisitors; +using System; + +namespace Luban.Job.Common.Types +{ + public class TArray : TType + { + public TType ElementType { get; } + + public TArray(TType elementType) : base(false) + { + ElementType = elementType; + } + + public override bool TryParseFrom(string s) + { + throw new NotSupportedException(); + } + + public override bool IsCollection => true; + + + public override void Apply<T>(ITypeActionVisitor<T> visitor, T x) + { + visitor.Accept(this, x); + } + + public override void Apply<T1, T2>(ITypeActionVisitor<T1, T2> visitor, T1 x, T2 y) + { + visitor.Accept(this, x, y); + } + + public override TR Apply<TR>(ITypeFuncVisitor<TR> visitor) + { + return visitor.Accept(this); + } + + public override TR Apply<T, TR>(ITypeFuncVisitor<T, TR> visitor, T x) + { + return visitor.Accept(this, x); + } + + public override TR Apply<T1, T2, TR>(ITypeFuncVisitor<T1, T2, TR> visitor, T1 x, T2 y) + { + return visitor.Accept(this, x, y); + } + + public override TR Apply<T1, T2, T3, TR>(ITypeFuncVisitor<T1, T2, T3, TR> visitor, T1 x, T2 y, T3 z) + { + return visitor.Accept(this, x, y, z); + } + } +} diff --git a/src/Luban.Job.Common/Source/Types/TBean.cs b/src/Luban.Job.Common/Source/Types/TBean.cs new file mode 100644 index 0000000..db5b305 --- /dev/null +++ b/src/Luban.Job.Common/Source/Types/TBean.cs @@ -0,0 +1,56 @@ +using Luban.Job.Common.Defs; +using Luban.Job.Common.TypeVisitors; +using System; + +namespace Luban.Job.Common.Types +{ + public class TBean : TType + { + public DefBeanBase Bean { get; set; } + + public T GetBeanAs<T>() where T : DefBeanBase => (T)Bean; + + // TODO bean 允许指定是否可空 + public TBean(DefBeanBase defBean, bool isNullable) : base(defBean.IsAbstractType) + { + this.Bean = defBean; + } + + public override bool TryParseFrom(string s) + { + throw new NotSupportedException(); + } + + public bool IsDynamic => Bean.IsAbstractType; + + public override void Apply<T>(ITypeActionVisitor<T> visitor, T x) + { + visitor.Accept(this, x); + } + + public override void Apply<T1, T2>(ITypeActionVisitor<T1, T2> visitor, T1 x, T2 y) + { + visitor.Accept(this, x, y); + } + + public override TR Apply<TR>(ITypeFuncVisitor<TR> visitor) + { + return visitor.Accept(this); + } + + public override TR Apply<T, TR>(ITypeFuncVisitor<T, TR> visitor, T x) + { + return visitor.Accept(this, x); + } + + public override TR Apply<T1, T2, TR>(ITypeFuncVisitor<T1, T2, TR> visitor, T1 x, T2 y) + { + return visitor.Accept(this, x, y); + } + + public override TR Apply<T1, T2, T3, TR>(ITypeFuncVisitor<T1, T2, T3, TR> visitor, T1 x, T2 y, T3 z) + { + return visitor.Accept(this, x, y, z); + } + } +} diff --git a/src/Luban.Job.Common/Source/Types/TBool.cs b/src/Luban.Job.Common/Source/Types/TBool.cs new file mode 100644 index 0000000..d891840 --- /dev/null +++ b/src/Luban.Job.Common/Source/Types/TBool.cs @@ -0,0 +1,53 @@ +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Common.Types +{ + public class TBool : TType + { + + public static TBool Ins { get; } = new TBool(false); + + public static TBool NullableIns { get; } = new TBool(true); + + public TBool(bool isNullable) : base(isNullable) + { + } + + public bool IsBool => true; + + public override bool TryParseFrom(string s) + { + return bool.TryParse(s, out _); + } + + public override void Apply<T>(ITypeActionVisitor<T> visitor, T x) + { + visitor.Accept(this, x); + } + + public override void Apply<T1, T2>(ITypeActionVisitor<T1, T2> visitor, T1 x, T2 y) + { + visitor.Accept(this, x, y); + } + + public override TR Apply<TR>(ITypeFuncVisitor<TR> visitor) + { + return visitor.Accept(this); + } + + public override TR Apply<T, TR>(ITypeFuncVisitor<T, TR> visitor, T x) + { + return visitor.Accept(this, x); + } + + public override TR Apply<T1, T2, TR>(ITypeFuncVisitor<T1, T2, TR> visitor, T1 x, T2 y) + { + return visitor.Accept(this, x, y); + } + + public override TR Apply<T1, T2, T3, TR>(ITypeFuncVisitor<T1, T2, T3, TR> visitor, T1 x, T2 y, T3 z) + { + return visitor.Accept(this, x, y, z); + } + } +} diff --git a/src/Luban.Job.Common/Source/Types/TByte.cs b/src/Luban.Job.Common/Source/Types/TByte.cs new file mode 100644 index 0000000..c567b03 --- /dev/null +++ b/src/Luban.Job.Common/Source/Types/TByte.cs @@ -0,0 +1,50 @@ +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Common.Types +{ + public class TByte : TType + { + public static TByte Ins { get; } = new TByte(false); + + public static TByte NullableIns { get; } = new TByte(true); + + public TByte(bool isNullable) : base(isNullable) + { + } + + public override bool TryParseFrom(string s) + { + return byte.TryParse(s, out _); + } + + public override void Apply<T>(ITypeActionVisitor<T> visitor, T x) + { + visitor.Accept(this, x); + } + + public override void Apply<T1, T2>(ITypeActionVisitor<T1, T2> visitor, T1 x, T2 y) + { + visitor.Accept(this, x, y); + } + + public override TR Apply<TR>(ITypeFuncVisitor<TR> visitor) + { + return visitor.Accept(this); + } + + public override TR Apply<T, TR>(ITypeFuncVisitor<T, TR> visitor, T x) + { + return visitor.Accept(this, x); + } + + public override TR Apply<T1, T2, TR>(ITypeFuncVisitor<T1, T2, TR> visitor, T1 x, T2 y) + { + return visitor.Accept(this, x, y); + } + + public override TR Apply<T1, T2, T3, TR>(ITypeFuncVisitor<T1, T2, T3, TR> visitor, T1 x, T2 y, T3 z) + { + return visitor.Accept(this, x, y, z); + } + } +} diff --git a/src/Luban.Job.Common/Source/Types/TBytes.cs b/src/Luban.Job.Common/Source/Types/TBytes.cs new file mode 100644 index 0000000..68253b8 --- /dev/null +++ b/src/Luban.Job.Common/Source/Types/TBytes.cs @@ -0,0 +1,49 @@ +using Luban.Job.Common.TypeVisitors; +using System; + +namespace Luban.Job.Common.Types +{ + public class TBytes : TType + { + public static TBytes Ins { get; } = new TBytes(false); + + public TBytes(bool isNullable) : base(isNullable) + { + } + + public override bool TryParseFrom(string s) + { + throw new NotSupportedException(); + } + + public override void Apply<T>(ITypeActionVisitor<T> visitor, T x) + { + visitor.Accept(this, x); + } + + public override void Apply<T1, T2>(ITypeActionVisitor<T1, T2> visitor, T1 x, T2 y) + { + visitor.Accept(this, x, y); + } + + public override TR Apply<TR>(ITypeFuncVisitor<TR> visitor) + { + return visitor.Accept(this); + } + + public override TR Apply<T, TR>(ITypeFuncVisitor<T, TR> visitor, T x) + { + return visitor.Accept(this, x); + } + + public override TR Apply<T1, T2, TR>(ITypeFuncVisitor<T1, T2, TR> visitor, T1 x, T2 y) + { + return visitor.Accept(this, x, y); + } + + public override TR Apply<T1, T2, T3, TR>(ITypeFuncVisitor<T1, T2, T3, TR> visitor, T1 x, T2 y, T3 z) + { + return visitor.Accept(this, x, y, z); + } + } +} diff --git a/src/Luban.Job.Common/Source/Types/TDateTime.cs b/src/Luban.Job.Common/Source/Types/TDateTime.cs new file mode 100644 index 0000000..b707bf9 --- /dev/null +++ b/src/Luban.Job.Common/Source/Types/TDateTime.cs @@ -0,0 +1,52 @@ +using Luban.Job.Common.TypeVisitors; +using System; + +namespace Luban.Job.Common.Types +{ + public class TDateTime : TType + { + public static TDateTime Ins { get; } = new TDateTime(false); + + public static TDateTime NullableIns { get; } = new TDateTime(true); + + public TDateTime(bool isNullable) : base(isNullable) + { + } + + public override bool TryParseFrom(string s) + { + throw new NotSupportedException(); + } + + public override void Apply<T>(ITypeActionVisitor<T> visitor, T x) + { + visitor.Accept(this, x); + } + + public override void Apply<T1, T2>(ITypeActionVisitor<T1, T2> visitor, T1 x, T2 y) + { + visitor.Accept(this, x, y); + } + + public override TR Apply<TR>(ITypeFuncVisitor<TR> visitor) + { + return visitor.Accept(this); + } + + public override TR Apply<T, TR>(ITypeFuncVisitor<T, TR> visitor, T x) + { + return visitor.Accept(this, x); + } + + public override TR Apply<T1, T2, TR>(ITypeFuncVisitor<T1, T2, TR> visitor, T1 x, T2 y) + { + return visitor.Accept(this, x, y); + } + + public override TR Apply<T1, T2, T3, TR>(ITypeFuncVisitor<T1, T2, T3, TR> visitor, T1 x, T2 y, T3 z) + { + return visitor.Accept(this, x, y, z); + } + + } +} diff --git a/src/Luban.Job.Common/Source/Types/TDouble.cs b/src/Luban.Job.Common/Source/Types/TDouble.cs new file mode 100644 index 0000000..24a7d0b --- /dev/null +++ b/src/Luban.Job.Common/Source/Types/TDouble.cs @@ -0,0 +1,50 @@ +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Common.Types +{ + public class TDouble : TType + { + public static TDouble Ins { get; } = new TDouble(false); + + public static TDouble NullableIns { get; } = new TDouble(true); + + public TDouble(bool isNullable) : base(isNullable) + { + } + + public override bool TryParseFrom(string s) + { + return double.TryParse(s, out _); + } + + public override void Apply<T>(ITypeActionVisitor<T> visitor, T x) + { + visitor.Accept(this, x); + } + + public override void Apply<T1, T2>(ITypeActionVisitor<T1, T2> visitor, T1 x, T2 y) + { + visitor.Accept(this, x, y); + } + + public override TR Apply<TR>(ITypeFuncVisitor<TR> visitor) + { + return visitor.Accept(this); + } + + public override TR Apply<T, TR>(ITypeFuncVisitor<T, TR> visitor, T x) + { + return visitor.Accept(this, x); + } + + public override TR Apply<T1, T2, TR>(ITypeFuncVisitor<T1, T2, TR> visitor, T1 x, T2 y) + { + return visitor.Accept(this, x, y); + } + + public override TR Apply<T1, T2, T3, TR>(ITypeFuncVisitor<T1, T2, T3, TR> visitor, T1 x, T2 y, T3 z) + { + return visitor.Accept(this, x, y, z); + } + } +} diff --git a/src/Luban.Job.Common/Source/Types/TEnum.cs b/src/Luban.Job.Common/Source/Types/TEnum.cs new file mode 100644 index 0000000..418f195 --- /dev/null +++ b/src/Luban.Job.Common/Source/Types/TEnum.cs @@ -0,0 +1,51 @@ +using Luban.Job.Common.Defs; +using Luban.Job.Common.TypeVisitors; +using System; + +namespace Luban.Job.Common.Types +{ + public class TEnum : TType + { + public DefEnum DefineEnum { get; } + + public TEnum(DefEnum defEnum, bool isNullable) : base(isNullable) + { + this.DefineEnum = defEnum; + } + + public override bool TryParseFrom(string s) + { + throw new NotSupportedException(); + } + + public override void Apply<T>(ITypeActionVisitor<T> visitor, T x) + { + visitor.Accept(this, x); + } + + public override void Apply<T1, T2>(ITypeActionVisitor<T1, T2> visitor, T1 x, T2 y) + { + visitor.Accept(this, x, y); + } + + public override TR Apply<TR>(ITypeFuncVisitor<TR> visitor) + { + return visitor.Accept(this); + } + + public override TR Apply<T, TR>(ITypeFuncVisitor<T, TR> visitor, T x) + { + return visitor.Accept(this, x); + } + + public override TR Apply<T1, T2, TR>(ITypeFuncVisitor<T1, T2, TR> visitor, T1 x, T2 y) + { + return visitor.Accept(this, x, y); + } + + public override TR Apply<T1, T2, T3, TR>(ITypeFuncVisitor<T1, T2, T3, TR> visitor, T1 x, T2 y, T3 z) + { + return visitor.Accept(this, x, y, z); + } + } +} diff --git a/src/Luban.Job.Common/Source/Types/TFint.cs b/src/Luban.Job.Common/Source/Types/TFint.cs new file mode 100644 index 0000000..3947ace --- /dev/null +++ b/src/Luban.Job.Common/Source/Types/TFint.cs @@ -0,0 +1,50 @@ +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Common.Types +{ + public class TFint : TType + { + public static TFint Ins { get; } = new TFint(false); + + public static TFint NullableIns { get; } = new TFint(true); + + public TFint(bool isNullable) : base(isNullable) + { + } + + public override bool TryParseFrom(string s) + { + return int.TryParse(s, out _); + } + + public override void Apply<T>(ITypeActionVisitor<T> visitor, T x) + { + visitor.Accept(this, x); + } + + public override void Apply<T1, T2>(ITypeActionVisitor<T1, T2> visitor, T1 x, T2 y) + { + visitor.Accept(this, x, y); + } + + public override TR Apply<TR>(ITypeFuncVisitor<TR> visitor) + { + return visitor.Accept(this); + } + + public override TR Apply<T, TR>(ITypeFuncVisitor<T, TR> visitor, T x) + { + return visitor.Accept(this, x); + } + + public override TR Apply<T1, T2, TR>(ITypeFuncVisitor<T1, T2, TR> visitor, T1 x, T2 y) + { + return visitor.Accept(this, x, y); + } + + public override TR Apply<T1, T2, T3, TR>(ITypeFuncVisitor<T1, T2, T3, TR> visitor, T1 x, T2 y, T3 z) + { + return visitor.Accept(this, x, y, z); + } + } +} diff --git a/src/Luban.Job.Common/Source/Types/TFloat.cs b/src/Luban.Job.Common/Source/Types/TFloat.cs new file mode 100644 index 0000000..a6aebb7 --- /dev/null +++ b/src/Luban.Job.Common/Source/Types/TFloat.cs @@ -0,0 +1,50 @@ +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Common.Types +{ + public class TFloat : TType + { + public static TFloat Ins { get; } = new TFloat(false); + + public static TFloat NullableIns { get; } = new TFloat(true); + + public TFloat(bool isNullable) : base(isNullable) + { + } + + public override bool TryParseFrom(string s) + { + return float.TryParse(s, out _); + } + + public override void Apply<T>(ITypeActionVisitor<T> visitor, T x) + { + visitor.Accept(this, x); + } + + public override void Apply<T1, T2>(ITypeActionVisitor<T1, T2> visitor, T1 x, T2 y) + { + visitor.Accept(this, x, y); + } + + public override TR Apply<TR>(ITypeFuncVisitor<TR> visitor) + { + return visitor.Accept(this); + } + + public override TR Apply<T, TR>(ITypeFuncVisitor<T, TR> visitor, T x) + { + return visitor.Accept(this, x); + } + + public override TR Apply<T1, T2, TR>(ITypeFuncVisitor<T1, T2, TR> visitor, T1 x, T2 y) + { + return visitor.Accept(this, x, y); + } + + public override TR Apply<T1, T2, T3, TR>(ITypeFuncVisitor<T1, T2, T3, TR> visitor, T1 x, T2 y, T3 z) + { + return visitor.Accept(this, x, y, z); + } + } +} diff --git a/src/Luban.Job.Common/Source/Types/TFlong.cs b/src/Luban.Job.Common/Source/Types/TFlong.cs new file mode 100644 index 0000000..c507c52 --- /dev/null +++ b/src/Luban.Job.Common/Source/Types/TFlong.cs @@ -0,0 +1,50 @@ +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Common.Types +{ + public class TFlong : TType + { + public static TFlong Ins { get; } = new TFlong(false); + + public static TFlong NullableIns { get; } = new TFlong(true); + + public TFlong(bool isNullable) : base(isNullable) + { + } + + public override bool TryParseFrom(string s) + { + return long.TryParse(s, out _); + } + + public override void Apply<T>(ITypeActionVisitor<T> visitor, T x) + { + visitor.Accept(this, x); + } + + public override void Apply<T1, T2>(ITypeActionVisitor<T1, T2> visitor, T1 x, T2 y) + { + visitor.Accept(this, x, y); + } + + public override TR Apply<TR>(ITypeFuncVisitor<TR> visitor) + { + return visitor.Accept(this); + } + + public override TR Apply<T, TR>(ITypeFuncVisitor<T, TR> visitor, T x) + { + return visitor.Accept(this, x); + } + + public override TR Apply<T1, T2, TR>(ITypeFuncVisitor<T1, T2, TR> visitor, T1 x, T2 y) + { + return visitor.Accept(this, x, y); + } + + public override TR Apply<T1, T2, T3, TR>(ITypeFuncVisitor<T1, T2, T3, TR> visitor, T1 x, T2 y, T3 z) + { + return visitor.Accept(this, x, y, z); + } + } +} diff --git a/src/Luban.Job.Common/Source/Types/TFshort.cs b/src/Luban.Job.Common/Source/Types/TFshort.cs new file mode 100644 index 0000000..8b9185e --- /dev/null +++ b/src/Luban.Job.Common/Source/Types/TFshort.cs @@ -0,0 +1,50 @@ +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Common.Types +{ + public class TFshort : TType + { + public static TFshort Ins { get; } = new TFshort(false); + + public static TFshort NullableIns { get; } = new TFshort(true); + + public TFshort(bool isNullable) : base(isNullable) + { + } + + public override bool TryParseFrom(string s) + { + return short.TryParse(s, out _); + } + + public override void Apply<T>(ITypeActionVisitor<T> visitor, T x) + { + visitor.Accept(this, x); + } + + public override void Apply<T1, T2>(ITypeActionVisitor<T1, T2> visitor, T1 x, T2 y) + { + visitor.Accept(this, x, y); + } + + public override TR Apply<TR>(ITypeFuncVisitor<TR> visitor) + { + return visitor.Accept(this); + } + + public override TR Apply<T, TR>(ITypeFuncVisitor<T, TR> visitor, T x) + { + return visitor.Accept(this, x); + } + + public override TR Apply<T1, T2, TR>(ITypeFuncVisitor<T1, T2, TR> visitor, T1 x, T2 y) + { + return visitor.Accept(this, x, y); + } + + public override TR Apply<T1, T2, T3, TR>(ITypeFuncVisitor<T1, T2, T3, TR> visitor, T1 x, T2 y, T3 z) + { + return visitor.Accept(this, x, y, z); + } + } +} diff --git a/src/Luban.Job.Common/Source/Types/TInt.cs b/src/Luban.Job.Common/Source/Types/TInt.cs new file mode 100644 index 0000000..5ce31e8 --- /dev/null +++ b/src/Luban.Job.Common/Source/Types/TInt.cs @@ -0,0 +1,50 @@ +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Common.Types +{ + public class TInt : TType + { + public static TInt Ins { get; } = new TInt(false); + + public static TInt NullableIns { get; } = new TInt(true); + + public TInt(bool isNullable) : base(isNullable) + { + } + + public override bool TryParseFrom(string s) + { + return int.TryParse(s, out _); + } + + public override void Apply<T>(ITypeActionVisitor<T> visitor, T x) + { + visitor.Accept(this, x); + } + + public override void Apply<T1, T2>(ITypeActionVisitor<T1, T2> visitor, T1 x, T2 y) + { + visitor.Accept(this, x, y); + } + + public override TR Apply<TR>(ITypeFuncVisitor<TR> visitor) + { + return visitor.Accept(this); + } + + public override TR Apply<T, TR>(ITypeFuncVisitor<T, TR> visitor, T x) + { + return visitor.Accept(this, x); + } + + public override TR Apply<T1, T2, TR>(ITypeFuncVisitor<T1, T2, TR> visitor, T1 x, T2 y) + { + return visitor.Accept(this, x, y); + } + + public override TR Apply<T1, T2, T3, TR>(ITypeFuncVisitor<T1, T2, T3, TR> visitor, T1 x, T2 y, T3 z) + { + return visitor.Accept(this, x, y, z); + } + } +} diff --git a/src/Luban.Job.Common/Source/Types/TList.cs b/src/Luban.Job.Common/Source/Types/TList.cs new file mode 100644 index 0000000..86f6ea2 --- /dev/null +++ b/src/Luban.Job.Common/Source/Types/TList.cs @@ -0,0 +1,55 @@ +using Luban.Job.Common.TypeVisitors; +using System; + +namespace Luban.Job.Common.Types +{ + public class TList : TType + { + public TType ElementType { get; } + + public bool IsArrayList { get; } + + public TList(TType elementType, bool isArrayList) : base(false) + { + ElementType = elementType; + IsArrayList = isArrayList; + } + + public override bool TryParseFrom(string s) + { + throw new NotSupportedException(); + } + + public override bool IsCollection => true; + + public override void Apply<T>(ITypeActionVisitor<T> visitor, T x) + { + visitor.Accept(this, x); + } + + public override void Apply<T1, T2>(ITypeActionVisitor<T1, T2> visitor, T1 x, T2 y) + { + visitor.Accept(this, x, y); + } + + public override TR Apply<TR>(ITypeFuncVisitor<TR> visitor) + { + return visitor.Accept(this); + } + + public override TR Apply<T, TR>(ITypeFuncVisitor<T, TR> visitor, T x) + { + return visitor.Accept(this, x); + } + + public override TR Apply<T1, T2, TR>(ITypeFuncVisitor<T1, T2, TR> visitor, T1 x, T2 y) + { + return visitor.Accept(this, x, y); + } + + public override TR Apply<T1, T2, T3, TR>(ITypeFuncVisitor<T1, T2, T3, TR> visitor, T1 x, T2 y, T3 z) + { + return visitor.Accept(this, x, y, z); + } + } +} diff --git a/src/Luban.Job.Common/Source/Types/TLong.cs b/src/Luban.Job.Common/Source/Types/TLong.cs new file mode 100644 index 0000000..e204ebc --- /dev/null +++ b/src/Luban.Job.Common/Source/Types/TLong.cs @@ -0,0 +1,50 @@ +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Common.Types +{ + public class TLong : TType + { + public static TLong Ins { get; } = new TLong(false); + + public static TLong NullableIns { get; } = new TLong(true); + + public TLong(bool isNullable) : base(isNullable) + { + } + + public override bool TryParseFrom(string s) + { + return long.TryParse(s, out _); + } + + public override void Apply<T>(ITypeActionVisitor<T> visitor, T x) + { + visitor.Accept(this, x); + } + + public override void Apply<T1, T2>(ITypeActionVisitor<T1, T2> visitor, T1 x, T2 y) + { + visitor.Accept(this, x, y); + } + + public override TR Apply<TR>(ITypeFuncVisitor<TR> visitor) + { + return visitor.Accept(this); + } + + public override TR Apply<T, TR>(ITypeFuncVisitor<T, TR> visitor, T x) + { + return visitor.Accept(this, x); + } + + public override TR Apply<T1, T2, TR>(ITypeFuncVisitor<T1, T2, TR> visitor, T1 x, T2 y) + { + return visitor.Accept(this, x, y); + } + + public override TR Apply<T1, T2, T3, TR>(ITypeFuncVisitor<T1, T2, T3, TR> visitor, T1 x, T2 y, T3 z) + { + return visitor.Accept(this, x, y, z); + } + } +} diff --git a/src/Luban.Job.Common/Source/Types/TMap.cs b/src/Luban.Job.Common/Source/Types/TMap.cs new file mode 100644 index 0000000..9753ba7 --- /dev/null +++ b/src/Luban.Job.Common/Source/Types/TMap.cs @@ -0,0 +1,58 @@ +using Luban.Job.Common.TypeVisitors; +using System; + +namespace Luban.Job.Common.Types +{ + public class TMap : TType + { + public TType KeyType { get; } + + public TType ValueType { get; } + + public bool IsOrderedMap { get; } + + public TMap(TType keyType, TType valueType, bool isOrderedMap) : base(false) + { + KeyType = keyType; + ValueType = valueType; + IsOrderedMap = isOrderedMap; + } + + public override bool TryParseFrom(string s) + { + throw new NotSupportedException(); + } + + public override bool IsCollection => true; + + public override void Apply<T>(ITypeActionVisitor<T> visitor, T x) + { + visitor.Accept(this, x); + } + + public override void Apply<T1, T2>(ITypeActionVisitor<T1, T2> visitor, T1 x, T2 y) + { + visitor.Accept(this, x, y); + } + + public override TR Apply<TR>(ITypeFuncVisitor<TR> visitor) + { + return visitor.Accept(this); + } + + public override TR Apply<T, TR>(ITypeFuncVisitor<T, TR> visitor, T x) + { + return visitor.Accept(this, x); + } + + public override TR Apply<T1, T2, TR>(ITypeFuncVisitor<T1, T2, TR> visitor, T1 x, T2 y) + { + return visitor.Accept(this, x, y); + } + + public override TR Apply<T1, T2, T3, TR>(ITypeFuncVisitor<T1, T2, T3, TR> visitor, T1 x, T2 y, T3 z) + { + return visitor.Accept(this, x, y, z); + } + } +} diff --git a/src/Luban.Job.Common/Source/Types/TSet.cs b/src/Luban.Job.Common/Source/Types/TSet.cs new file mode 100644 index 0000000..7a62acd --- /dev/null +++ b/src/Luban.Job.Common/Source/Types/TSet.cs @@ -0,0 +1,55 @@ +using Luban.Job.Common.TypeVisitors; +using System; + +namespace Luban.Job.Common.Types +{ + public class TSet : TType + { + public TType ElementType { get; } + + public bool IsOrderSet { get; } + + public TSet(TType elementType, bool isOrderSet) : base(false) + { + ElementType = elementType; + IsOrderSet = isOrderSet; + } + + public override bool TryParseFrom(string s) + { + throw new NotSupportedException(); + } + + public override bool IsCollection => true; + + public override void Apply<T>(ITypeActionVisitor<T> visitor, T x) + { + visitor.Accept(this, x); + } + + public override void Apply<T1, T2>(ITypeActionVisitor<T1, T2> visitor, T1 x, T2 y) + { + visitor.Accept(this, x, y); + } + + public override TR Apply<TR>(ITypeFuncVisitor<TR> visitor) + { + return visitor.Accept(this); + } + + public override TR Apply<T, TR>(ITypeFuncVisitor<T, TR> visitor, T x) + { + return visitor.Accept(this, x); + } + + public override TR Apply<T1, T2, TR>(ITypeFuncVisitor<T1, T2, TR> visitor, T1 x, T2 y) + { + return visitor.Accept(this, x, y); + } + + public override TR Apply<T1, T2, T3, TR>(ITypeFuncVisitor<T1, T2, T3, TR> visitor, T1 x, T2 y, T3 z) + { + return visitor.Accept(this, x, y, z); + } + } +} diff --git a/src/Luban.Job.Common/Source/Types/TShort.cs b/src/Luban.Job.Common/Source/Types/TShort.cs new file mode 100644 index 0000000..68e6fed --- /dev/null +++ b/src/Luban.Job.Common/Source/Types/TShort.cs @@ -0,0 +1,50 @@ +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Common.Types +{ + public class TShort : TType + { + public static TShort Ins { get; } = new TShort(false); + + public static TShort NullableIns { get; } = new TShort(true); + + public TShort(bool isNullable) : base(isNullable) + { + } + + public override bool TryParseFrom(string s) + { + return short.TryParse(s, out _); + } + + public override void Apply<T>(ITypeActionVisitor<T> visitor, T x) + { + visitor.Accept(this, x); + } + + public override void Apply<T1, T2>(ITypeActionVisitor<T1, T2> visitor, T1 x, T2 y) + { + visitor.Accept(this, x, y); + } + + public override TR Apply<TR>(ITypeFuncVisitor<TR> visitor) + { + return visitor.Accept(this); + } + + public override TR Apply<T, TR>(ITypeFuncVisitor<T, TR> visitor, T x) + { + return visitor.Accept(this, x); + } + + public override TR Apply<T1, T2, TR>(ITypeFuncVisitor<T1, T2, TR> visitor, T1 x, T2 y) + { + return visitor.Accept(this, x, y); + } + + public override TR Apply<T1, T2, T3, TR>(ITypeFuncVisitor<T1, T2, T3, TR> visitor, T1 x, T2 y, T3 z) + { + return visitor.Accept(this, x, y, z); + } + } +} diff --git a/src/Luban.Job.Common/Source/Types/TString.cs b/src/Luban.Job.Common/Source/Types/TString.cs new file mode 100644 index 0000000..2868016 --- /dev/null +++ b/src/Luban.Job.Common/Source/Types/TString.cs @@ -0,0 +1,53 @@ +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Common.Types +{ + public class TString : TType + { + public static TString Ins { get; } = new TString(false); + + public static TString NullableIns { get; } = new TString(true); + + public TString(bool isNullable) : base(isNullable) + { + } + + public override bool TryParseFrom(string s) + { + return true; + } + + public override void Apply<T>(ITypeActionVisitor<T> visitor, T x) + { + visitor.Accept(this, x); + } + + public override void Apply<T1, T2>(ITypeActionVisitor<T1, T2> visitor, T1 x, T2 y) + { + visitor.Accept(this, x, y); + } + + public override TR Apply<TR>(ITypeFuncVisitor<TR> visitor) + { + return visitor.Accept(this); + } + + public override TR Apply<T, TR>(ITypeFuncVisitor<T, TR> visitor, T x) + { + return visitor.Accept(this, x); + } + + + public override TR Apply<T1, T2, TR>(ITypeFuncVisitor<T1, T2, TR> visitor, T1 x, T2 y) + { + return visitor.Accept(this, x, y); + } + + public override TR Apply<T1, T2, T3, TR>(ITypeFuncVisitor<T1, T2, T3, TR> visitor, T1 x, T2 y, T3 z) + { + return visitor.Accept(this, x, y, z); + } + + + } +} diff --git a/src/Luban.Job.Common/Source/Types/TText.cs b/src/Luban.Job.Common/Source/Types/TText.cs new file mode 100644 index 0000000..5677609 --- /dev/null +++ b/src/Luban.Job.Common/Source/Types/TText.cs @@ -0,0 +1,50 @@ +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Common.Types +{ + public class TText : TType + { + public static TText Ins { get; } = new TText(false); + + public static TText NullableIns { get; } = new TText(true); + + public TText(bool isNullable) : base(isNullable) + { + } + + public override bool TryParseFrom(string s) + { + return true; + } + + public override void Apply<T>(ITypeActionVisitor<T> visitor, T x) + { + visitor.Accept(this, x); + } + + public override void Apply<T1, T2>(ITypeActionVisitor<T1, T2> visitor, T1 x, T2 y) + { + visitor.Accept(this, x, y); + } + + public override TR Apply<TR>(ITypeFuncVisitor<TR> visitor) + { + return visitor.Accept(this); + } + + public override TR Apply<T, TR>(ITypeFuncVisitor<T, TR> visitor, T x) + { + return visitor.Accept(this, x); + } + + public override TR Apply<T1, T2, TR>(ITypeFuncVisitor<T1, T2, TR> visitor, T1 x, T2 y) + { + return visitor.Accept(this, x, y); + } + + public override TR Apply<T1, T2, T3, TR>(ITypeFuncVisitor<T1, T2, T3, TR> visitor, T1 x, T2 y, T3 z) + { + return visitor.Accept(this, x, y, z); + } + } +} diff --git a/src/Luban.Job.Common/Source/Types/TType.cs b/src/Luban.Job.Common/Source/Types/TType.cs new file mode 100644 index 0000000..2a76e85 --- /dev/null +++ b/src/Luban.Job.Common/Source/Types/TType.cs @@ -0,0 +1,25 @@ +using Luban.Job.Common.TypeVisitors; + +namespace Luban.Job.Common.Types +{ + public abstract class TType + { + public bool IsNullable { get; } + + protected TType(bool isNullable) + { + IsNullable = isNullable; + } + + public abstract bool TryParseFrom(string s); + + public virtual bool IsCollection => false; + + public abstract void Apply<T>(ITypeActionVisitor<T> visitor, T x); + public abstract void Apply<T1, T2>(ITypeActionVisitor<T1, T2> visitor, T1 x, T2 y); + public abstract TR Apply<TR>(ITypeFuncVisitor<TR> visitor); + public abstract TR Apply<T, TR>(ITypeFuncVisitor<T, TR> visitor, T x); + public abstract TR Apply<T1, T2, TR>(ITypeFuncVisitor<T1, T2, TR> visitor, T1 x, T2 y); + public abstract TR Apply<T1, T2, T3, TR>(ITypeFuncVisitor<T1, T2, T3, TR> visitor, T1 x, T2 y, T3 z); + } +} diff --git a/src/Luban.Job.Common/Source/Types/TVector2.cs b/src/Luban.Job.Common/Source/Types/TVector2.cs new file mode 100644 index 0000000..695225f --- /dev/null +++ b/src/Luban.Job.Common/Source/Types/TVector2.cs @@ -0,0 +1,51 @@ +using Luban.Job.Common.TypeVisitors; +using System; + +namespace Luban.Job.Common.Types +{ + public class TVector2 : TType + { + public static TVector2 Ins { get; } = new TVector2(false); + + public static TVector2 NullableIns { get; } = new TVector2(true); + + public TVector2(bool isNullable) : base(isNullable) + { + } + + public override bool TryParseFrom(string s) + { + throw new NotSupportedException(); + } + + public override void Apply<T>(ITypeActionVisitor<T> visitor, T x) + { + visitor.Accept(this, x); + } + + public override void Apply<T1, T2>(ITypeActionVisitor<T1, T2> visitor, T1 x, T2 y) + { + visitor.Accept(this, x, y); + } + + public override TR Apply<TR>(ITypeFuncVisitor<TR> visitor) + { + return visitor.Accept(this); + } + + public override TR Apply<T, TR>(ITypeFuncVisitor<T, TR> visitor, T x) + { + return visitor.Accept(this, x); + } + + public override TR Apply<T1, T2, TR>(ITypeFuncVisitor<T1, T2, TR> visitor, T1 x, T2 y) + { + return visitor.Accept(this, x, y); + } + + public override TR Apply<T1, T2, T3, TR>(ITypeFuncVisitor<T1, T2, T3, TR> visitor, T1 x, T2 y, T3 z) + { + return visitor.Accept(this, x, y, z); + } + } +} diff --git a/src/Luban.Job.Common/Source/Types/TVector3.cs b/src/Luban.Job.Common/Source/Types/TVector3.cs new file mode 100644 index 0000000..4d69a35 --- /dev/null +++ b/src/Luban.Job.Common/Source/Types/TVector3.cs @@ -0,0 +1,51 @@ +using Luban.Job.Common.TypeVisitors; +using System; + +namespace Luban.Job.Common.Types +{ + public class TVector3 : TType + { + public static TVector3 Ins { get; } = new TVector3(false); + + public static TVector3 NullableIns { get; } = new TVector3(true); + + public TVector3(bool isNullable) : base(isNullable) + { + } + + public override bool TryParseFrom(string s) + { + throw new NotSupportedException(); + } + + public override void Apply<T>(ITypeActionVisitor<T> visitor, T x) + { + visitor.Accept(this, x); + } + + public override void Apply<T1, T2>(ITypeActionVisitor<T1, T2> visitor, T1 x, T2 y) + { + visitor.Accept(this, x, y); + } + + public override TR Apply<TR>(ITypeFuncVisitor<TR> visitor) + { + return visitor.Accept(this); + } + + public override TR Apply<T, TR>(ITypeFuncVisitor<T, TR> visitor, T x) + { + return visitor.Accept(this, x); + } + + public override TR Apply<T1, T2, TR>(ITypeFuncVisitor<T1, T2, TR> visitor, T1 x, T2 y) + { + return visitor.Accept(this, x, y); + } + + public override TR Apply<T1, T2, T3, TR>(ITypeFuncVisitor<T1, T2, T3, TR> visitor, T1 x, T2 y, T3 z) + { + return visitor.Accept(this, x, y, z); + } + } +} diff --git a/src/Luban.Job.Common/Source/Types/TVector4.cs b/src/Luban.Job.Common/Source/Types/TVector4.cs new file mode 100644 index 0000000..fd117e6 --- /dev/null +++ b/src/Luban.Job.Common/Source/Types/TVector4.cs @@ -0,0 +1,51 @@ +using Luban.Job.Common.TypeVisitors; +using System; + +namespace Luban.Job.Common.Types +{ + public class TVector4 : TType + { + public static TVector4 Ins { get; } = new TVector4(false); + + public static TVector4 NullableIns { get; } = new TVector4(true); + + public TVector4(bool isNullable) : base(isNullable) + { + } + + public override bool TryParseFrom(string s) + { + throw new NotSupportedException(); + } + + public override void Apply<T>(ITypeActionVisitor<T> visitor, T x) + { + visitor.Accept(this, x); + } + + public override void Apply<T1, T2>(ITypeActionVisitor<T1, T2> visitor, T1 x, T2 y) + { + visitor.Accept(this, x, y); + } + + public override TR Apply<TR>(ITypeFuncVisitor<TR> visitor) + { + return visitor.Accept(this); + } + + public override TR Apply<T, TR>(ITypeFuncVisitor<T, TR> visitor, T x) + { + return visitor.Accept(this, x); + } + + public override TR Apply<T1, T2, TR>(ITypeFuncVisitor<T1, T2, TR> visitor, T1 x, T2 y) + { + return visitor.Accept(this, x, y); + } + + public override TR Apply<T1, T2, T3, TR>(ITypeFuncVisitor<T1, T2, T3, TR> visitor, T1 x, T2 y, T3 z) + { + return visitor.Accept(this, x, y, z); + } + } +} diff --git a/src/Luban.Job.Common/Source/Utils/CacheFileUtil.cs b/src/Luban.Job.Common/Source/Utils/CacheFileUtil.cs new file mode 100644 index 0000000..87ba7cb --- /dev/null +++ b/src/Luban.Job.Common/Source/Utils/CacheFileUtil.cs @@ -0,0 +1,43 @@ +using Luban.Common.Protos; +using Luban.Common.Utils; +using Luban.Server.Common; +using System.Threading.Tasks; + +namespace Luban.Job.Common.Utils +{ + public static class CacheFileUtil + { + private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger(); + + public static async Task<byte[]> GetFileAsync(Bright.Net.ServiceModes.Managers.SessionBase session, string file, string md5) + { + var fileCache = CacheManager.Ins.FindCache(md5); + if (fileCache != null) + { + s_logger.Trace("find file:{file} md5:{md5} in cache.", file, md5); + return fileCache.Content; + } + else + { + var res = await session.CallRpcAsync<GetInputFile, GetInputFileArg, GetInputFileRes>(new GetInputFileArg() { File = file }, 30); + CacheManager.Ins.AddCache(file, md5, res.Content); + return res.Content; + } + } + + public static string GenMd5AndAddCache(string fileName, string content) + { + var bytes = System.Text.Encoding.UTF8.GetBytes(content); + var md5 = FileUtil.CalcMD5(bytes); + CacheManager.Ins.AddCache(fileName, md5, bytes); + return md5; + } + + public static string GenMd5AndAddCache(string fileName, byte[] bytes) + { + var md5 = FileUtil.CalcMD5(bytes); + CacheManager.Ins.AddCache(fileName, md5, bytes); + return md5; + } + } +} diff --git a/src/Luban.Job.Common/Source/Utils/FileHeaderUtil.cs b/src/Luban.Job.Common/Source/Utils/FileHeaderUtil.cs new file mode 100644 index 0000000..d04a329 --- /dev/null +++ b/src/Luban.Job.Common/Source/Utils/FileHeaderUtil.cs @@ -0,0 +1,46 @@ +namespace Luban.Job.Common.Utils +{ + public static class FileHeaderUtil + { + const string AUTO_GENERATE_C_LIKE = @" +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ +"; + + + const string AUTO_GENERATE_LUA = @" +--[[------------------------------------------------------------------------------ +-- <auto-generated> +-- This code was generated by a tool. +-- Changes to this file may cause incorrect behavior and will be lost if +-- the code is regenerated. +-- </auto-generated> +--]]------------------------------------------------------------------------------ +"; + + const string AUTO_GENERATE_PYTHON = @" +''' + <auto-generated> + This code was generated by a tool. + Changes to this file may cause incorrect behavior and will be lost if + the code is regenerated. + </auto-generated> +''' +"; + + public static string ConcatAutoGenerationHeader(string txt, ELanguage lan) + { + switch (lan) + { + case ELanguage.LUA: return AUTO_GENERATE_LUA + txt; + case ELanguage.PYTHON: return AUTO_GENERATE_PYTHON + txt; + default: return AUTO_GENERATE_C_LIKE + txt; + } + } + } +} diff --git a/src/Luban.Job.Common/Source/Utils/RenderFileUtil.cs b/src/Luban.Job.Common/Source/Utils/RenderFileUtil.cs new file mode 100644 index 0000000..2ef0b55 --- /dev/null +++ b/src/Luban.Job.Common/Source/Utils/RenderFileUtil.cs @@ -0,0 +1,88 @@ +using System; + +namespace Luban.Job.Common.Utils +{ + public static class RenderFileUtil + { + + public static string GetDefTypePath(string fullName, ELanguage lan) + { + switch (lan) + { + case ELanguage.CS: return fullName.Replace('.', '/') + ".cs"; + case ELanguage.JAVA: return fullName.Replace('.', '/') + ".java"; + case ELanguage.CPP: return fullName + ".cpp"; + case ELanguage.GO: return fullName + ".go"; + case ELanguage.LUA: return fullName.Replace('.', '_') + ".lua"; + case ELanguage.JS: return fullName + ".js"; + case ELanguage.TYPESCRIPT: return fullName.Replace('.', '/') + ".ts"; + default: throw new NotSupportedException(); + } + + } + + public static string GetCsDefTypePath(string fullName) + { + return fullName.Replace('.', '/') + ".cs"; + } + + public static string GetGoDefTypePath(string fullName) + { + return fullName + ".go"; + } + + public static string GetUeCppDefTypeHeaderFilePath(string fullName) + { + return fullName.Replace('.', '_') + ".h"; + } + + public static string GetUeCppDefTypeHeaderFilePathWithoutSuffix(string fullName) + { + return fullName.Replace('.', '_'); + } + + + public static string GetCppDefTypeCppFilePath(string fullName) + { + return fullName + ".cpp"; + } + + public static string GetCppDefTypeHeadFilePath(string fullName) + { + return fullName + ".h"; + } + + public static string GetTableDataPath(string fullName) + { + return fullName + ".bin"; + } + + public static bool IsExcelFile(string fullName) + { + return fullName.EndsWith(".csv", StringComparison.Ordinal) + || fullName.EndsWith(".xls", StringComparison.Ordinal) + || fullName.EndsWith(".xlsx", StringComparison.Ordinal); + } + + public static (string, string) SplitFileAndSheetName(string url) + { + int sheetSepIndex = url.IndexOf('@'); + if (sheetSepIndex < 0) + { + return (url, null); + } + else + { + int lastPathSep = url.LastIndexOf('/', sheetSepIndex); + if (lastPathSep >= 0) + { + return (url[0..(lastPathSep + 1)] + url[(sheetSepIndex + 1)..], url[(lastPathSep + 1)..sheetSepIndex]); + } + else + { + return (url[(sheetSepIndex + 1)..], url[(lastPathSep + 1)..sheetSepIndex]); + } + } + } + } +} diff --git a/src/Luban.Job.Common/Source/Utils/RenderUtil.cs b/src/Luban.Job.Common/Source/Utils/RenderUtil.cs new file mode 100644 index 0000000..fcb4c17 --- /dev/null +++ b/src/Luban.Job.Common/Source/Utils/RenderUtil.cs @@ -0,0 +1,233 @@ +using Luban.Job.Common.Defs; +using Scriban; +using System; + +namespace Luban.Job.Common.Utils +{ + public static class RenderUtil + { + [ThreadStatic] + private static Template t_constRender; + public static string RenderCsConstClass(DefConst c) + { + var ctx = new TemplateContext(); + var env = new TTypeTemplateCommonExtends + { + ["x"] = c + }; + ctx.PushGlobal(env); + + + var template = t_constRender ??= Template.Parse(@" +namespace {{x.namespace_with_top_module}} +{ + + public sealed class {{x.name}} + { + {{~ for item in x.items ~}} + public const {{cs_define_type item.ctype}} {{item.name}} = {{cs_const_value item.ctype item.value}}; + {{~end~}} + } +} + +"); + var result = template.Render(ctx); + + return result; + } + + [ThreadStatic] + private static Template t_enumRender; + public static string RenderCsEnumClass(DefEnum e) + { + var template = t_enumRender ??= Template.Parse(@" +namespace {{namespace_with_top_module}} +{ + {{-if is_flags}} + [System.Flags] + {{-end}} + public enum {{name}} + { + {{~ for item in items ~}} + {{item.name}} = {{item.value}}, + {{~end~}} + } +} + + +"); + var result = template.Render(e); + + return result; + } + + [ThreadStatic] + private static Template t_javaConstRender; + public static string RenderJavaConstClass(DefConst c) + { + var ctx = new TemplateContext(); + var env = new TTypeTemplateCommonExtends + { + ["x"] = c + }; + ctx.PushGlobal(env); + + + var template = t_javaConstRender ??= Template.Parse(@" +package {{x.namespace_with_top_module}}; + +public final class {{x.name}} +{ + {{~ for item in x.items ~}} + public static final {{java_define_type item.ctype}} {{item.name}} = {{java_const_value item.ctype item.value}}; + {{~end~}} +} + + +"); + var result = template.Render(ctx); + + return result; + } + + [ThreadStatic] + private static Template t_javaEnumRender; + public static string RenderJavaEnumClass(DefEnum e) + { + var template = t_javaEnumRender ??= Template.Parse(@" +package {{namespace_with_top_module}}; + +public enum {{name}} +{ + {{~ for item in items ~}} + {{item.name}}({{item.value}}), + {{~end~}} + ; + + private final int value; + + public int getValue() { + return value; + } + + {{name}}(int value) { + this.value = value; + } + + public static {{name}} valueOf(int value) { + {{~ for item in items ~}} + if (value == {{item.value}}) return {{item.name}}; + {{~end~}} + throw new IllegalArgumentException(""""); + } +} + +"); + var result = template.Render(e); + + return result; + } + [ThreadStatic] + private static Template t_cppConstRender; + public static string RenderCppConstClass(DefConst c) + { + var ctx = new TemplateContext(); + var env = new TTypeTemplateCommonExtends + { + ["x"] = c + }; + ctx.PushGlobal(env); + + + var template = t_cppConstRender ??= Template.Parse(@" +{{x.cpp_namespace_begin}} + +struct {{x.name}} +{ + {{~ for item in x.items ~}} + static constexpr {{cpp_define_type item.ctype}} {{item.name}} = {{cpp_const_value item.ctype item.value}}; + {{~end~}} +}; +{{x.cpp_namespace_end}} + +"); + var result = template.Render(ctx); + + return result; + } + + [ThreadStatic] + private static Template t_cppEnumRender; + public static string RenderCppEnumClass(DefEnum e) + { + var template = t_cppEnumRender ??= Template.Parse(@" +{{cpp_namespace_begin}} + +enum class {{name}} +{ + {{~ for item in items ~}} + {{item.name}} = {{item.value}}, + {{~end~}} +}; +{{cpp_namespace_end}} +"); + var result = template.Render(e); + + return result; + } + + + + + [ThreadStatic] + private static Template t_tsConstRender; + public static string RenderTsConstClass(DefConst c) + { + var ctx = new TemplateContext(); + var env = new TTypeTemplateCommonExtends + { + ["x"] = c + }; + ctx.PushGlobal(env); + + + var template = t_tsConstRender ??= Template.Parse(@" +namespace {{x.namespace_with_top_module}} +{ + export class {{x.name}} + { + {{~ for item in x.items ~}} + static {{item.name}} : {{ts_define_type item.ctype}} = {{ts_const_value item.ctype item.value}}; + {{~end~}} + } +} + +"); + var result = template.Render(ctx); + + return result; + } + + [ThreadStatic] + private static Template t_tsEnumRender; + public static string RenderTsEnumClass(DefEnum e) + { + var template = t_tsEnumRender ??= Template.Parse(@" +namespace {{namespace_with_top_module}} +{ + export enum {{name}} + { + {{- for item in items }} + {{item.name}} = {{item.value}}, + {{-end}} + } +} + + +"); + var result = template.Render(e); + + return result; + } + } +} diff --git a/src/Luban.Server.Common/Luban.Server.Common.csproj b/src/Luban.Server.Common/Luban.Server.Common.csproj new file mode 100644 index 0000000..ff856f2 --- /dev/null +++ b/src/Luban.Server.Common/Luban.Server.Common.csproj @@ -0,0 +1,11 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>netcoreapp3.1</TargetFramework> + </PropertyGroup> + + <ItemGroup> + <ProjectReference Include="..\Luban.Common\Luban.Common.csproj" /> + </ItemGroup> + +</Project> diff --git a/src/Luban.Server.Common/Source/CacheManager.cs b/src/Luban.Server.Common/Source/CacheManager.cs new file mode 100644 index 0000000..f02b380 --- /dev/null +++ b/src/Luban.Server.Common/Source/CacheManager.cs @@ -0,0 +1,137 @@ +using Bright.Threading; +using Bright.Time; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Luban.Server.Common +{ + public class FileCache + { + public string MD5 { get; set; } + + public string FileName { get; set; } + + public byte[] Content { get; set; } + + public int LastAccessTime { get; set; } + + public long Timestamp { get; set; } + } + + public class CacheManager + { + private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger(); + + private static readonly AtomicLong s_timestampAlloc = new AtomicLong(); + + public static CacheManager Ins { get; } = new CacheManager(); + + + private readonly Dictionary<string, FileCache> _caches = new Dictionary<string, FileCache>(); + + public long CacheHighWaterMarkSize { get; set; } = 4L * 1024L * 1024L * 1024L; // 1g + + public long CacheLowWaterMarkSize { get; set; } = 3L * 1024 * 1024L * 1024L; // 1g + + public long TotalBytes { get; private set; } + + + public void Reset() + { + lock (this) + { + _caches.Clear(); + TotalBytes = 0; + } + } + + public FileCache FindCache(string MD5, bool updateTimestamp = true) + { + lock (this) + { + if (_caches.TryGetValue(MD5, out var cache)) + { + if (updateTimestamp) + { + UpdateAccess(cache); + } + return cache; + } + else + { + return null; + } + } + } + + public FileCache TestGetCache(string MD5) + { + lock (this) + { + return _caches.TryGetValue(MD5, out var cache) ? cache : null; + } + } + + private static void UpdateAccess(FileCache cache) + { + cache.Timestamp = s_timestampAlloc.IncrementAndGet(); + } + + + public void AddCache(string fileName, string MD5, byte[] content) + { + lock (this) + { + if (_caches.ContainsKey(MD5)) + { + _caches[MD5].Timestamp = s_timestampAlloc.IncrementAndGet(); + } + else + { + var cache = new FileCache() + { + FileName = fileName, + MD5 = MD5, + Content = content, + LastAccessTime = TimeUtil.Now, + Timestamp = s_timestampAlloc.IncrementAndGet(), + }; + _caches[MD5] = cache; + TotalBytes += content.Length; + + if (TotalBytes > CacheHighWaterMarkSize) + { + RemoveLRU(); + } + } + } + + } + + private void RemoveLRU() + { + lock (this) + { + s_logger.Info("before remove expires. cache nums:{num} total bytes:{bytes}", _caches.Count, TotalBytes); + + var sortCacheByTimestamp = _caches.Values.ToList(); + sortCacheByTimestamp.Sort((a, b) => a.Timestamp.CompareTo(b.Timestamp)); + + long lowWaterMarkSize = Math.Min(CacheLowWaterMarkSize, (long)(CacheHighWaterMarkSize * 0.9f)); + foreach (var c in sortCacheByTimestamp) + { + if (TotalBytes <= lowWaterMarkSize) + { + break; + } + + _caches.Remove(c.MD5, out var _); + TotalBytes -= c.Content.Length; + s_logger.Info("remove cache. file:{file} md5:{md5} size:{size}, total bytes:{bytes} after remove.", c.FileName, c.MD5, c.Content.Length, TotalBytes); + } + s_logger.Info("after remove expires. cache nums:{num} total bytes:{bytes}", _caches.Count, TotalBytes); + } + } + } +} diff --git a/src/Luban.Server.Common/Source/ControllerAttribute.cs b/src/Luban.Server.Common/Source/ControllerAttribute.cs new file mode 100644 index 0000000..ac29f74 --- /dev/null +++ b/src/Luban.Server.Common/Source/ControllerAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace Luban.Server.Common +{ + [AttributeUsage(AttributeTargets.Class)] + public class ControllerAttribute : System.Attribute + { + public string Name { get; } + + public ControllerAttribute(string name) + { + Name = name; + } + } +} diff --git a/src/Luban.Server.Common/Source/IJobController.cs b/src/Luban.Server.Common/Source/IJobController.cs new file mode 100644 index 0000000..9b08f8e --- /dev/null +++ b/src/Luban.Server.Common/Source/IJobController.cs @@ -0,0 +1,10 @@ +using Luban.Common.Protos; +using System.Threading.Tasks; + +namespace Luban.Server.Common +{ + public interface IJobController + { + Task GenAsync(RemoteAgent agent, GenJob rpc); + } +} diff --git a/src/Luban.Server.Common/Source/RemoteAgent.cs b/src/Luban.Server.Common/Source/RemoteAgent.cs new file mode 100644 index 0000000..9a24d3b --- /dev/null +++ b/src/Luban.Server.Common/Source/RemoteAgent.cs @@ -0,0 +1,186 @@ +using Bright.Net.ServiceModes.Managers; +using Bright.Time; +using Luban.Common.Protos; +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.Serialization; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Luban.Server.Common +{ + public class ReadRemoteFailException : Exception + { + public ReadRemoteFailException() + { + } + + public ReadRemoteFailException(string message) : base(message) + { + } + + public ReadRemoteFailException(string message, Exception innerException) : base(message, innerException) + { + } + + protected ReadRemoteFailException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + public class RemoteAgent + { + private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger(); + + public SessionBase Session { get; } + + private readonly bool _trace; + + private readonly Dictionary<string, Task<byte[]>> _remoteReadAllBytesTasks = new Dictionary<string, Task<byte[]>>(); + + private readonly Dictionary<string, Task<GetImportFileOrDirectoryRes>> _getImportFileOrDirTasks = new Dictionary<string, Task<GetImportFileOrDirectoryRes>>(); + + public RemoteAgent(SessionBase session, bool trace) + { + Session = session; + _trace = trace; + } + + private const int GET_INPUT_FILE_TIMEOUT = 10; + + public async Task<byte[]> GetFromCacheOrReadAllBytesAsync(string file, string md5) + { + var cache = CacheManager.Ins.FindCache(md5); + if (cache != null) + { + return cache.Content; + } + var content = await ReadAllBytesAsync(file).ConfigureAwait(false); + CacheManager.Ins.AddCache(file, md5, content); + return content; + } + + public Task<byte[]> ReadAllBytesAsync(string file) + { + lock (_remoteReadAllBytesTasks) + { + if (!_remoteReadAllBytesTasks.TryGetValue(file, out var task)) + { + task = Task.Run(async () => + { + long t1 = TimeUtil.NowMillis; + var res = await Session.CallRpcAsync<GetInputFile, GetInputFileArg, GetInputFileRes>(new GetInputFileArg() { File = file }, GET_INPUT_FILE_TIMEOUT); + if (res.Err != Luban.Common.EErrorCode.OK) + { + throw new ReadRemoteFailException($"{res.Err}"); + } + s_logger.Info("read remote file:{file} cost:{time}", file, TimeUtil.NowMillis - t1); + return res.Content; + }); + task.ConfigureAwait(false); + _remoteReadAllBytesTasks.Add(file, task); + } + return task; + } + + } + + public Task<GetImportFileOrDirectoryRes> GetFileOrDirectoryAsync(string file) + { + lock (_getImportFileOrDirTasks) + { + if (!_getImportFileOrDirTasks.TryGetValue(file, out var task)) + { + task = Task.Run(async () => + { + long t1 = TimeUtil.NowMillis; + var res = await Session.CallRpcAsync<GetImportFileOrDirectory, GetImportFileOrDirectoryArg, GetImportFileOrDirectoryRes>( + new GetImportFileOrDirectoryArg() { FileOrDirName = file }, + GET_INPUT_FILE_TIMEOUT); + if (res.Err != Luban.Common.EErrorCode.OK) + { + throw new ReadRemoteFailException($"{res.Err}"); + } + s_logger.Trace("read GetFileOrDirectoryAsync end. file:{file} cost:{time}", file, TimeUtil.NowMillis - t1); + return res; + }); + _getImportFileOrDirTasks.Add(file, task); + } + return task; + } + + } + + const int QUERY_FILE_EXISTS_TIMEOUT = 10; + + public async Task<QueryFilesExistsRes> QueryFileExistsAsync(QueryFilesExistsArg arg) + { + long t1 = TimeUtil.NowMillis; + var res = await Session.CallRpcAsync<QueryFilesExists, QueryFilesExistsArg, QueryFilesExistsRes>(arg, QUERY_FILE_EXISTS_TIMEOUT); + + s_logger.Trace("query file exists. count:{count} cost:{time}", arg.Files.Count, TimeUtil.NowMillis - t1); + return res; + } + + public async Task<XElement> OpenXmlAsync(string xmlFile) + { + try + { + s_logger.Trace("open {xml}", xmlFile); + return XElement.Load(new MemoryStream(await ReadAllBytesAsync(xmlFile))); + } + catch (Exception e) + { + throw new Exception($"打开定义文件:{xmlFile} 失败 --> {e.Message}"); + } + } + + + #region log + + public void Error(string fmt, params object[] objs) + { + Log("error", string.Format(fmt, objs)); + } + + public void Error(Exception e, string s) + { + LogException(e, s); + } + + public void Error(Exception e, string fmt, params object[] objs) + { + LogException(e, string.Format(fmt, objs)); + } + + public void Info(string fmt, params object[] objs) + { + Log("info", string.Format(fmt, objs)); + } + + public void Info(string s) + { + Log("info", s); + } + + public void Trace(string fmt, params object[] objs) + { + if (_trace) + { + Log("trace", string.Format(fmt, objs)); + } + } + + private void Log(string level, string content) + { + Session.Send(new PushLog() { Level = level, LogContent = content }); + } + + private void LogException(Exception e, string content) + { + Session.Send(new PushException() { LogContent = content, Message = e.Message, StackTrace = e.StackTrace }); + } + #endregion + } +} diff --git a/src/Luban.Server/.editorconfig b/src/Luban.Server/.editorconfig new file mode 100644 index 0000000..a4b9046 --- /dev/null +++ b/src/Luban.Server/.editorconfig @@ -0,0 +1,7 @@ +[*.cs] + +# CA1305: Specify IFormatProvider +dotnet_diagnostic.CA1305.severity = none + +# CA1303: Do not pass literals as localized parameters +dotnet_diagnostic.CA1303.severity = none diff --git a/src/Luban.Server/Dockerfile b/src/Luban.Server/Dockerfile new file mode 100644 index 0000000..73c8fdf --- /dev/null +++ b/src/Luban.Server/Dockerfile @@ -0,0 +1,37 @@ +FROM mcr.microsoft.com/dotnet/core/sdk:3.1 as build + +WORKDIR /app/Luban.Common +COPY Luban.Common/*.csproj ./ +COPY Luban.Common/Source ./Source +COPY Luban.Common/.editorconfig . +COPY nuget.config ./nuget.config + +WORKDIR /app/Luban.Server.Common +COPY Luban.Server.Common/*.csproj ./ +COPY Luban.Server.Common/Source ./Source +COPY nuget.config ./nuget.config + +WORKDIR /app/Luban.Job.Common +COPY Luban.Job.Common/*.csproj ./ +COPY Luban.Job.Common/Source ./Source +COPY nuget.config ./nuget.config + +WORKDIR /app/Luban.Job.Cfg +COPY Luban.Job.Cfg/*.csproj ./ +COPY Luban.Job.Cfg/Source ./Source +COPY nuget.config ./nuget.config + +WORKDIR /app/Luban.Server +COPY Luban.Server/Luban.Server.csproj ./ +COPY Luban.Server/.editorconfig . +COPY Luban.Server/Source ./Source +COPY nuget.config ./nuget.config + +RUN dotnet publish -c Release -o out + +FROM mcr.microsoft.com/dotnet/core/runtime:3.1 AS runtime +RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo "Asia/Shanghai" > /etc/timezone +WORKDIR /app +COPY --from=build /app/Luban.Server/out ./ +EXPOSE 8899/tcp +ENTRYPOINT ["/app/Luban.Server", "-p", "8899"] \ No newline at end of file diff --git a/src/Luban.Server/Luban.Server.csproj b/src/Luban.Server/Luban.Server.csproj new file mode 100644 index 0000000..27e19e9 --- /dev/null +++ b/src/Luban.Server/Luban.Server.csproj @@ -0,0 +1,36 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <OutputType>Exe</OutputType> + <TargetFramework>netcoreapp3.1</TargetFramework> + </PropertyGroup> + + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> + <NoWarn>1701;1702;;NU1701</NoWarn> + <DefineConstants>TRACE;EMBED_CFG</DefineConstants> + </PropertyGroup> + + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> + <NoWarn>1701;1702;;NU1701</NoWarn> + <DefineConstants>TRACE;EMBED_CFG</DefineConstants> + </PropertyGroup> + + <ItemGroup> + <Compile Remove="Datas\**" /> + <EmbeddedResource Remove="Datas\**" /> + <None Remove="Datas\**" /> + </ItemGroup> + + <ItemGroup> + <PackageReference Include="CommandLineParser" Version="2.8.0" /> + <PackageReference Include="ExcelDataReader" Version="3.6.0" /> + <PackageReference Include="NeoLua" Version="1.3.11" /> + <PackageReference Include="Scriban" Version="2.1.4" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\Luban.Job.Cfg\Luban.Job.Cfg.csproj" /> + <ProjectReference Include="..\Luban.Server.Common\Luban.Server.Common.csproj" /> + </ItemGroup> + +</Project> diff --git a/src/Luban.Server/Properties/launchSettings.json b/src/Luban.Server/Properties/launchSettings.json new file mode 100644 index 0000000..c4a30f2 --- /dev/null +++ b/src/Luban.Server/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "Luban.Server": { + "commandName": "Project", + "commandLineArgs": "-p 8899" + } + } +} \ No newline at end of file diff --git a/src/Luban.Server/Source/GenServer.cs b/src/Luban.Server/Source/GenServer.cs new file mode 100644 index 0000000..514b4b4 --- /dev/null +++ b/src/Luban.Server/Source/GenServer.cs @@ -0,0 +1,46 @@ +using Bright.Net;
using Bright.Net.Bootstraps; +using Bright.Net.Channels; +using Bright.Net.Codecs;
using Bright.Net.ServiceModes.Managers; +using Luban.Common; +using Luban.Common.Protos; +using Luban.Server.Common; +using System;
using System.Collections.Generic;
using System.Net; + +namespace Luban.Server
{ + public class Session : SessionBase
 {
 public override void OnActive()
 {
 }

 public override void OnInactive()
 {
 }
 }

 public class GenServer : ServerManager<Session>
 {
 private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();

 public static GenServer Ins { get; } = new GenServer();

 private readonly Dictionary<int, Action<Session, Protocol>> _handlers = new Dictionary<int, Action<Session, Protocol>>();

 private readonly Dictionary<string, IJobController> _jobs = new Dictionary<string, IJobController>();

 public void Start(int port, Dictionary<int, ProtocolCreator> factories)
 { + _handlers.Add(GetOutputFile.ID, (s, p) => OnGetOutputFile(s, (GetOutputFile)p)); + _handlers.Add(GenJob.ID, (s, p) => OnGenJob(s, (GenJob)p)); + + var worker = new EventLoopGroup(4, 16);
 var server = new TcpServerBootstrap
 {
 LocalAddress = new IPEndPoint(IPAddress.Any, port),

 ChildrenEventLoopGroup = worker,
 EventLoop = worker.ChooseEventLoop(),

 InitChildrenHandler = (s, c) =>
 { + c.Pipeline.AddLast(new ProtocolFrameCodec(20_000_000, new RecycleByteBufPool(100, 100), new DefaultProtocolAllocator(factories))); + c.Pipeline.AddLast(this); + }
 };

 _ = server.ListenAsync();
 }

 public void RegisterJob(string jobType, IJobController jobController) + { + s_logger.Info("register job. name:{name} class:{class}", jobType, jobController.GetType().FullName); + _jobs.Add(jobType, jobController); + }

 protected override void OnAddSession(Session s)
 {

 }

 protected override void OnRemoveSession(Session s)
 {

 }

 protected override void HandleProtocol(Session session, Protocol proto)
 {
 s_logger.Trace("session:{id} protocol:{@proto}", session.Id, proto);
 if (_handlers.TryGetValue(proto.GetTypeId(), out var handler)) + { + handler(session, proto); + }
 else + { + s_logger.Error("unknown proto:{proto}", proto); + }
 } + + private void OnGetOutputFile(Session session, GetOutputFile rpc)
 {
 var cache = CacheManager.Ins.FindCache(rpc.Arg.MD5);
 session.ReplyRpc<GetOutputFile, GetOutputFileArg, GetOutputFileRes>(rpc, new GetOutputFileRes()
 {
 Exists = cache != null,
 FileContent = cache?.Content,
 });
 }


 private void OnGenJob(Session session, GenJob rpc) + { + s_logger.Info("onGenJob. arg:{@arg}", rpc.Arg); + + if (_jobs.TryGetValue(rpc.Arg.JobType, out var jobController)) + { + _ = jobController.GenAsync(new RemoteAgent(session, rpc.Arg.Verbose), rpc); + } + else + { + session.ReplyRpc<GenJob, GenJobArg, GenJobRes>(rpc, new GenJobRes() + { + ErrCode = EErrorCode.UNKNOWN_JOB_TYPE, + ErrMsg = "unknown job type", + FileGroups = new List<FileGroup>(), + }); + } + }
 }
}
 \ No newline at end of file diff --git a/src/Luban.Server/Source/Program.cs b/src/Luban.Server/Source/Program.cs new file mode 100644 index 0000000..c81157a --- /dev/null +++ b/src/Luban.Server/Source/Program.cs @@ -0,0 +1,53 @@ +using Bright.Common; +using CommandLine; +using Luban.Common.Protos; +using Luban.Common.Utils; +using Luban.Server.Common; +using System; +using System.IO; +using System.Reflection; +using System.Threading; + +namespace Luban.Server +{ + class Program + { + class CommandLineOptions + { + [Option('p', "port", Required = false, HelpText = "listen port")] + public int Port { get; set; } = 8899; + } + + private static CommandLineOptions options; + + static void Main(string[] args) + { + var parseResult = Parser.Default.ParseArguments<CommandLineOptions>(args); + + parseResult.WithNotParsed(errs => + { + Environment.Exit(-1); + }); + + parseResult.WithParsed(opts => + { + options = opts; + }); + + Luban.Common.Utils.LogUtil.InitSimpleNLogConfigure(NLog.LogLevel.Info); + + System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance); + + GenServer.Ins.Start(options.Port, ProtocolStub.Factories); +#if EMBED_CFG + GenServer.Ins.RegisterJob("cfg", new Luban.Job.Cfg.JobController()); +#endif + int processorCount = System.Environment.ProcessorCount; + ThreadPool.SetMinThreads(Math.Max(4, processorCount), 5); + ThreadPool.SetMaxThreads(Math.Max(16, processorCount * 4), 10); + + Thread.CurrentThread.Join(); + } + + } +} diff --git a/src/Luban.Server/build_docker.bat b/src/Luban.Server/build_docker.bat new file mode 100644 index 0000000..71ba5d9 --- /dev/null +++ b/src/Luban.Server/build_docker.bat @@ -0,0 +1,2 @@ +docker build -t luban.server:latest -f Dockerfile .. +pause \ No newline at end of file diff --git a/src/Luban.sln b/src/Luban.sln new file mode 100644 index 0000000..e94de77 --- /dev/null +++ b/src/Luban.sln @@ -0,0 +1,55 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29418.71 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Luban.Client", "Luban.Client\Luban.Client.csproj", "{856BAA3D-B3B9-46FC-A960-A33B02C54FE5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Luban.Common", "Luban.Common\Luban.Common.csproj", "{2244DE30-3A9F-4A72-8C6F-277811D98473}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Luban.Job.Cfg", "Luban.Job.Cfg\Luban.Job.Cfg.csproj", "{9AC73673-5A5C-4CBF-B0FA-F9EAAE36EDC3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Luban.Job.Common", "Luban.Job.Common\Luban.Job.Common.csproj", "{30E7638B-1BBF-40B3-804E-889AD24907B7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Luban.Server.Common", "Luban.Server.Common\Luban.Server.Common.csproj", "{1C7311DF-8B66-4E8B-BF05-CA9B06C3D3E2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Luban.Server", "Luban.Server\Luban.Server.csproj", "{714B78D2-A3BE-4845-AF0E-0A5076B8E77E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {856BAA3D-B3B9-46FC-A960-A33B02C54FE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {856BAA3D-B3B9-46FC-A960-A33B02C54FE5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {856BAA3D-B3B9-46FC-A960-A33B02C54FE5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {856BAA3D-B3B9-46FC-A960-A33B02C54FE5}.Release|Any CPU.Build.0 = Release|Any CPU + {2244DE30-3A9F-4A72-8C6F-277811D98473}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2244DE30-3A9F-4A72-8C6F-277811D98473}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2244DE30-3A9F-4A72-8C6F-277811D98473}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2244DE30-3A9F-4A72-8C6F-277811D98473}.Release|Any CPU.Build.0 = Release|Any CPU + {9AC73673-5A5C-4CBF-B0FA-F9EAAE36EDC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9AC73673-5A5C-4CBF-B0FA-F9EAAE36EDC3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9AC73673-5A5C-4CBF-B0FA-F9EAAE36EDC3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9AC73673-5A5C-4CBF-B0FA-F9EAAE36EDC3}.Release|Any CPU.Build.0 = Release|Any CPU + {30E7638B-1BBF-40B3-804E-889AD24907B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {30E7638B-1BBF-40B3-804E-889AD24907B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {30E7638B-1BBF-40B3-804E-889AD24907B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {30E7638B-1BBF-40B3-804E-889AD24907B7}.Release|Any CPU.Build.0 = Release|Any CPU + {1C7311DF-8B66-4E8B-BF05-CA9B06C3D3E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1C7311DF-8B66-4E8B-BF05-CA9B06C3D3E2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1C7311DF-8B66-4E8B-BF05-CA9B06C3D3E2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1C7311DF-8B66-4E8B-BF05-CA9B06C3D3E2}.Release|Any CPU.Build.0 = Release|Any CPU + {714B78D2-A3BE-4845-AF0E-0A5076B8E77E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {714B78D2-A3BE-4845-AF0E-0A5076B8E77E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {714B78D2-A3BE-4845-AF0E-0A5076B8E77E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {714B78D2-A3BE-4845-AF0E-0A5076B8E77E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E62A389E-FD26-4B63-BE70-46C8D436FB5F} + EndGlobalSection +EndGlobal diff --git a/src/nuget.config b/src/nuget.config new file mode 100644 index 0000000..05097ed --- /dev/null +++ b/src/nuget.config @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<configuration> + <packageSources> + <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" /> + </packageSources> + <packageRestore> + <add key="enabled" value="True" /> + <add key="automatic" value="True" /> + </packageRestore> + <bindingRedirects> + <add key="skip" value="False" /> + </bindingRedirects> + <packageManagement> + <add key="format" value="0" /> + <add key="disabled" value="False" /> + </packageManagement> +</configuration> \ No newline at end of file