[init] add initial project files
parent
6c7dbb23f1
commit
3a3ab46fac
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
[//]: # (Author: bug)
|
||||||
|
[//]: # (Date: 2020-10-20 20:24:07)
|
||||||
|
|
||||||
|
# Gen
|
||||||
|
|
||||||
|
## 什么是 Gen
|
||||||
|
|
||||||
|
Gen 是一个强大的生成与缓存工具,用于但不限于 游戏配置、消息、资源格式转换 之类的生成。
|
||||||
|
|
||||||
|
相比传统简单的以excel为中心的表格导出工具,它提供了一个**完整的游戏配置数据解决方案**,有以下功能:
|
||||||
|
>
|
||||||
|
> * 数据定义
|
||||||
|
> * 数据编辑
|
||||||
|
> * 数据导出
|
||||||
|
> * 前后端代码生成
|
||||||
|
> * 本地化
|
||||||
|
> * 编辑器数据load&save代码生成
|
||||||
|
|
||||||
|
Gen能够良好满足小型、中型、大型及超大型游戏项目的配置需求。
|
||||||
|
|
||||||
|
Gen 工具不仅适用于游戏行业,也非常适合传统的互联网项目。
|
||||||
|
|
||||||
|
|
||||||
|
## 文档
|
||||||
|
* [主页](https://focus-creative-games.github.io/bright_gen/index.html)
|
||||||
|
* 各语言的简介: [English](README.en-us.md), [简体中文](README.md)
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
* Lua 使用示例
|
||||||
|
``` Lua
|
||||||
|
local data = require("TbDataFromJson")
|
||||||
|
local cfg = data[32]
|
||||||
|
print(cfg.name)
|
||||||
|
```
|
||||||
|
|
||||||
|
* [更多语言的例子](docs/samples.md)
|
||||||
|
|
||||||
|
## 特性
|
||||||
|
* [完备的数据类型支持](docs/feature.md#支持的数据类型)
|
||||||
|
* [多类型数据源支持](docs/feature.md#多数据源支持)
|
||||||
|
* [多种数据表模式](docs/feature.md#多种数据表模式)
|
||||||
|
* [按组导出数据](docs/feature.md#如何自定义导出分组)
|
||||||
|
* [生成速度快](docs/feature.md#生成极快)
|
||||||
|
* [增强 Excel 的表达](docs/feature.md#增强的-excel-格式)
|
||||||
|
* [代码提示支持](docs/feature.md#代码编辑器支持)
|
||||||
|
* [根据开发效率需求定制的数据输出格式](docs/feature.md#支持多种导出数据格式)
|
||||||
|
* [本地化支持](docs/feature.md#本地化支持)
|
||||||
|
* [代码提示支持](docs/feature.md#代码编辑器支持)
|
||||||
|
* [强大的数据校验能力](docs/feature.md#强大的数据校验能力)
|
||||||
|
* [资源导出支持](docs/feature.md#资源导出支持)
|
||||||
|
* [自动代码生成](docs/feature.md#优秀的代码生成)
|
||||||
|
* [数据分组](docs/feature.md#良好的数据组织)
|
||||||
|
* [多语言支持](docs/feature.md#支持的语言-覆盖主流的语言)
|
||||||
|
* [多服务器引擎支持](docs/feature.md#支持的服务器引擎-满足语言版本的情况下)
|
||||||
|
* [多客户端引擎支持](docs/feature.md#支持的客户端引擎-满足语言版本的情况下)
|
||||||
|
* [扩展能力](docs/feature.md#强大的扩展能力)
|
||||||
|
* [ ] 提供定制开发服务 ^_^
|
||||||
|
|
||||||
|
## RoadMap
|
||||||
|
- [ ] 新增 unity 内置编辑器
|
||||||
|
- [ ] 新增 unreal 内置编辑器
|
||||||
|
- [ ] 补充单元测试
|
||||||
|
- [x] 支持 python
|
||||||
|
|
||||||
|
## 布署
|
||||||
|
TODO
|
||||||
|
|
||||||
|
## 开发环境架设
|
||||||
|
* 安装 VS2019 社区版
|
||||||
|
* 安装 .dotnet core sdk 3.1
|
||||||
|
|
||||||
|
## 如何贡献?
|
||||||
|
* [Contributing](CONTRIBUTING.md) explains what kinds of changes we welcome
|
||||||
|
- [Workflow Instructions](docs/workflow/README.md) explains how to build and test
|
||||||
|
|
||||||
|
## Useful Links
|
||||||
|
|
||||||
|
* [.NET Core source index](https://source.dot.net)
|
||||||
|
* 社区的其它实现
|
||||||
|
* [tabtoy](https://github.com/davyxu/tabtoy)
|
||||||
|
|
||||||
|
## 支持和联系
|
||||||
|
QQ 群: 692890842
|
||||||
|
邮箱: taojingjian#gmail.com
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Birght Gen is licensed under the [MIT](LICENSE.TXT) license.
|
||||||
Binary file not shown.
|
|
@ -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}]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
return
|
||||||
|
{
|
||||||
|
x1 = false,
|
||||||
|
x2 = 2,
|
||||||
|
x3 = 128,
|
||||||
|
x4 = 1122,
|
||||||
|
x5 = 112233445566,
|
||||||
|
x6 = 1.3,
|
||||||
|
x7 = 1122,
|
||||||
|
x8 = 12,
|
||||||
|
x8_0 = 13,
|
||||||
|
x9 = 123,
|
||||||
|
x10 = "yf",
|
||||||
|
x12 = {x1=1},
|
||||||
|
x13 = "D",
|
||||||
|
x14 = { __type__="DemoD2", x1 = 1, x2=3},
|
||||||
|
v2 = {x= 1,y = 2},
|
||||||
|
v3 = {x=0.1, y= 0.2,z=0.3},
|
||||||
|
v4 = {x=1,y=2,z=3.5,w=4},
|
||||||
|
t1 = "1970-01-01 00:00:00",
|
||||||
|
k1 = {1,2},
|
||||||
|
k2 = {2,3},
|
||||||
|
k3 = {3,4},
|
||||||
|
k4 = {1,2},
|
||||||
|
k5 = {1,3},
|
||||||
|
k6 = {1,2},
|
||||||
|
k7 = {1,8},
|
||||||
|
k8 = {[2]=10,[3]=12},
|
||||||
|
k9 = {{y1=1,y2=true}, {y1=10,y2=false}},
|
||||||
|
k15 = {{ __type__="DemoD2", x1 = 1, x2=3}},
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,12 @@
|
||||||
|
##,align:true,row:true,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
__type__,x1,x2,x3,x4,x5,x6,x7,x8,x8_0,x9,x10,x11,x12,x13,,,k1,k2,k3,k4,k5,k6,k7,k8
|
||||||
|
,½ûÖ¹,x2:byte,x3:short,x4:int,x5:long, x6:float,x7:double,,,,,,,,,,array:int,array:int,array:int,array:int,array:int,array:int,array:int,map:int:int
|
||||||
|
DemoD2,TRUE,5,5,10000,13234234234,1.28,1.23457891,1234,1234,111111111,huang,,1988,A,,,"1,2,3","1,2,4","1,2,5","1,2,6","1,2,7","1,2,8","1,2,9","1,2,3,4"
|
||||||
|
,FALSE,0,6,198704,34523452345,2.5,19870421.2,453234,-345,112233445566 ,qiang,,1987,B,,,"2,4,6","2,4,7","2,4,8","2,4,9","2,4,10","2,4,11","2,4,12","1,10,2,20"
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
Can't render this file because it contains an unexpected character in line 4 and column 152.
|
Binary file not shown.
Binary file not shown.
|
|
@ -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}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
<data>
|
||||||
|
<x1>true</x1>
|
||||||
|
<x2>4</x2>
|
||||||
|
<x3>128</x3>
|
||||||
|
<x4>1122</x4>
|
||||||
|
<x5>112233445566</x5>
|
||||||
|
<x6>1.3</x6>
|
||||||
|
<x7>1112232.43123</x7>
|
||||||
|
<x8>112233</x8>
|
||||||
|
<x8_0>123</x8_0>
|
||||||
|
<x9>112334</x9>
|
||||||
|
<x10>yf</x10>
|
||||||
|
<x12>
|
||||||
|
<x1>1</x1>
|
||||||
|
</x12>
|
||||||
|
<x13>C</x13>
|
||||||
|
<x14 __type__="DemoD2">
|
||||||
|
<x1>1</x1>
|
||||||
|
<x2>2</x2>
|
||||||
|
</x14>
|
||||||
|
|
||||||
|
<v2>1,2</v2>
|
||||||
|
<v3>1.2,2.3,3.4</v3>
|
||||||
|
<v4>1.2,2.2,3.2,4.3</v4>
|
||||||
|
|
||||||
|
<t1>1970-01-01 00:00:00</t1>
|
||||||
|
|
||||||
|
<k1>
|
||||||
|
<item>1</item>
|
||||||
|
<item>2</item>
|
||||||
|
</k1>
|
||||||
|
<k2>
|
||||||
|
<item>1</item>
|
||||||
|
<item>2</item>
|
||||||
|
</k2>
|
||||||
|
<k3>
|
||||||
|
<item>1</item>
|
||||||
|
<item>2</item>
|
||||||
|
</k3>
|
||||||
|
<k4>
|
||||||
|
<item>1</item>
|
||||||
|
<item>2</item>
|
||||||
|
</k4>
|
||||||
|
<k5>
|
||||||
|
<item>1</item>
|
||||||
|
<item>2</item>
|
||||||
|
</k5>
|
||||||
|
<k6>
|
||||||
|
<item>1</item>
|
||||||
|
<item>2</item>
|
||||||
|
</k6>
|
||||||
|
<k7>
|
||||||
|
<item>1</item>
|
||||||
|
<item>3</item>
|
||||||
|
</k7>
|
||||||
|
|
||||||
|
<k8>
|
||||||
|
<item> <key>2</key><value>10</value></item>
|
||||||
|
<item> <key>3</key><value>30</value></item>
|
||||||
|
</k8>
|
||||||
|
|
||||||
|
<k9>
|
||||||
|
<item>
|
||||||
|
<y1>1</y1>
|
||||||
|
<y2>true</y2>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<y1>2</y1>
|
||||||
|
<y2>false</y2>
|
||||||
|
</item>
|
||||||
|
</k9>
|
||||||
|
<k15>
|
||||||
|
<item __type__="DemoD2">
|
||||||
|
<x1>1</x1>
|
||||||
|
<x2>2</x2>
|
||||||
|
</item>
|
||||||
|
</k15>
|
||||||
|
</data>
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
<root>
|
||||||
|
|
||||||
|
<topmodule name="cfg"/>
|
||||||
|
|
||||||
|
<group name="client,c" default="1"/>
|
||||||
|
<group name="server,s" default="1"/>
|
||||||
|
<group name="editor,e" default="1"/>
|
||||||
|
<group name="ue,u"/>
|
||||||
|
|
||||||
|
<import name="."/>
|
||||||
|
|
||||||
|
<service name="server" manager="Tables" group="s,server"/>
|
||||||
|
|
||||||
|
<service name="client" manager="Tables" group="c,client"/>
|
||||||
|
|
||||||
|
<service name="all" manager="Tables" group="c,s"/>
|
||||||
|
|
||||||
|
<service name="ue4_editor" manager="Tables" group="e"/>
|
||||||
|
|
||||||
|
<service name="unity_editor" manager="Tables" group="e"/>
|
||||||
|
|
||||||
|
<service name="ue4" manager="Tables" group="u,ue"/>
|
||||||
|
</root>
|
||||||
|
|
@ -0,0 +1,212 @@
|
||||||
|
<module name="test">
|
||||||
|
<const name="DemoConst">
|
||||||
|
<var name="x1" type="int" value="0"/>
|
||||||
|
<var name="x2" type="long" value="3242"/>
|
||||||
|
<var name="x3" type="float" value="444.3"/>
|
||||||
|
<var name="x4" type="double" value="55.3"/>
|
||||||
|
<!--var name="x5" type="string" value="abcdefg"/-->
|
||||||
|
</const>
|
||||||
|
|
||||||
|
<enum name="DemoEnum">
|
||||||
|
<var name="A" value="1"/>
|
||||||
|
<var name="B"/>
|
||||||
|
<var name="C" value="4"/>
|
||||||
|
<var name="D"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<bean name="DemoType1">
|
||||||
|
<var name="x1" type="int"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean name="DemoDynamic"> 多态数据结构
|
||||||
|
<var name="x1" type="int"/>
|
||||||
|
|
||||||
|
<bean name="DemoD2" alias="测试别名">
|
||||||
|
<var name="x2" type="int"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean name="DemoD3">
|
||||||
|
<var name="x3" type="int"/>
|
||||||
|
<bean name="DemoE1">
|
||||||
|
<var name="x4" type="int"/>
|
||||||
|
</bean>
|
||||||
|
</bean>
|
||||||
|
<bean name="DemoD5">
|
||||||
|
<var name="time" type="DateTimeRange"/>
|
||||||
|
</bean>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean name="DemoE2">
|
||||||
|
<var name="y1" type="int"/>
|
||||||
|
<var name="y2" type="bool"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean name="DemoType2" >
|
||||||
|
<var name="x1" type="bool"/>
|
||||||
|
<var name="x2" type="byte"/>
|
||||||
|
<var name="x3" type="short" ref="test.TbFullTypes"/>
|
||||||
|
<var name="x4" type="int?" convert="DemoEnum"/>
|
||||||
|
<var name="x5" type="long" convert="DemoEnum"/>
|
||||||
|
<var name="x6" type="float"/>
|
||||||
|
<var name="x7" type="double"/>
|
||||||
|
<var name="x8_0" type="fshort"/>
|
||||||
|
<var name="x8" type="fint"/>
|
||||||
|
<var name="x9" type="flong"/>
|
||||||
|
|
||||||
|
<var name="x10" type="string" path="normal;*.txt"/>
|
||||||
|
<var name="x12" type="DemoType1"/>
|
||||||
|
<var name="x13" type="DemoEnum"/>
|
||||||
|
<var name="x14" type="DemoDynamic" sep=","/>多态数据结构
|
||||||
|
|
||||||
|
<var name="v2" type="vector2"/>
|
||||||
|
<var name="v3" type="vector3"/>
|
||||||
|
<var name="v4" type="vector4"/>
|
||||||
|
|
||||||
|
<var name="t1" type="datetime"/>
|
||||||
|
|
||||||
|
<var name="k1" type="array,int"/> 使用;来分隔
|
||||||
|
|
||||||
|
<var name="k2" type="list,int"/>
|
||||||
|
<var name="k3" type="linkedlist,int"/>
|
||||||
|
<var name="k4" type="arraylist,int"/>
|
||||||
|
<var name="k5" type="set,int"/>
|
||||||
|
<var name="k6" type="treeset,int"/>
|
||||||
|
<var name="k7" type="hashset,int"/>
|
||||||
|
<var name="k8" type="map,int,int"/>
|
||||||
|
<var name="k9" type="list,DemoE2" sep="," index="y1"/>
|
||||||
|
<var name="k15" type="array,DemoDynamic" sep=","/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<table name="TbFullTypes" index="x3" value="DemoType2" input="test/full_type.xlsx"/> 最常见的普通 key-value表
|
||||||
|
|
||||||
|
|
||||||
|
<bean name="DateTimeRange" sep=";">
|
||||||
|
<var name="start_time" type="datetime"/>
|
||||||
|
<var name="end_time" type="datetime"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean name="DemoSingletonType">
|
||||||
|
<var name="id" type="int"/>
|
||||||
|
<var name="name" type="text"/>
|
||||||
|
<var name="date" type="DemoDynamic"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<table name="TbSingleton" mode="one" value="DemoSingletonType" input="test/table_one.xlsx"/> 单例表,只有一个记录
|
||||||
|
|
||||||
|
<table name="TbDataFromJson" value="DemoType2" input="test/json_datas"/> 普通表,不过数据从tbrole_datas目录递归读入,每个文件是一个记录
|
||||||
|
|
||||||
|
<table name="TbDataFromXml" value="DemoType2" input="test/xml_datas"/> 普通表,不过数据从tbrole_datas目录递归读入,每个文件是一个记录
|
||||||
|
|
||||||
|
<table name="TbDataFromLua" value="DemoType2" input="test/lua_datas"/> 普通表,不过数据从tbrole_datas目录递归读入,每个文件是一个记录
|
||||||
|
|
||||||
|
<table name="TbTwoKey" value="DemoType2" index="x3,x4" input="test/json_datas" mode="bmap"/>
|
||||||
|
|
||||||
|
|
||||||
|
<bean name="MultiRowType1">
|
||||||
|
<var name="id" type="int"/>
|
||||||
|
<var name="x" type="int"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean name="MultiRowType2">
|
||||||
|
<var name="id" type="int"/>
|
||||||
|
<var name="x" type="int"/>
|
||||||
|
<var name="y" type="float"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean name="MultiRowRecord">
|
||||||
|
<var name="id" type="int"/>
|
||||||
|
<var name="name" type="string"/>
|
||||||
|
<var name="one_rows" type="list,MultiRowType1"/>
|
||||||
|
<var name="multi_rows1" type="list,MultiRowType1" multi_rows="1"/>
|
||||||
|
<var name="multi_rows2" type="array,MultiRowType1" multi_rows="1"/>
|
||||||
|
<var name="multi_rows3" type="set,MultiRowType2" multi_rows="1"/>
|
||||||
|
<var name="multi_rows4" type="map,int,MultiRowType2" multi_rows="1"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<table name="TbMultiRowRecord" value="MultiRowRecord" input="test/multi_rows_record.xlsx"/>
|
||||||
|
|
||||||
|
|
||||||
|
<enum name="ETestUeType">
|
||||||
|
<var name="WHITE" alias="白"/>
|
||||||
|
<var name="BLACK"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<enum name="ETestEmptyEnum">
|
||||||
|
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<enum name="ETestEmptyEnum2">
|
||||||
|
<var name="SMALL_THAN_256" value="255"/>
|
||||||
|
<var name="X_256" value="256"/>
|
||||||
|
<var name="X_257" value="257"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<bean name="TestUeType">
|
||||||
|
<var name="x1" type="bool"/>
|
||||||
|
<var name="x2" type="byte"/>
|
||||||
|
<!--var name="x3" type="short"/-->
|
||||||
|
<var name="x4" type="int"/>
|
||||||
|
<var name="x5" type="long"/>
|
||||||
|
<var name="x6" type="float"/>
|
||||||
|
<!--var name="x7" type="double"/>
|
||||||
|
<var name="x8_0" type="fshort"/>
|
||||||
|
<var name="x8" type="fint"/>
|
||||||
|
<var name="x9" type="flong"/-->
|
||||||
|
|
||||||
|
<var name="x10" type="string"/>
|
||||||
|
<var name="x12" type="DemoType1"/>
|
||||||
|
<var name="x13" type="ETestUeType"/>
|
||||||
|
<!--var name="x14" type="DemoDynamic" sep=","/-->多态数据结构
|
||||||
|
|
||||||
|
<var name="v2" type="vector2"/>
|
||||||
|
<var name="v3" type="vector3"/>
|
||||||
|
<var name="v4" type="vector4"/>
|
||||||
|
|
||||||
|
<var name="t1" type="datetime"/>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
<var name="x15" type="DemoType1"/>
|
||||||
|
|
||||||
|
<var name="y1" type="int?"/> nullable ,多态结构肯定是nullable的
|
||||||
|
-->
|
||||||
|
|
||||||
|
<var name="k1" type="array,int"/> 使用;来分隔
|
||||||
|
|
||||||
|
<var name="k2" type="list,int" group="c,s"/>
|
||||||
|
<var name="k3" type="linkedlist,int"/>
|
||||||
|
<var name="k4" type="arraylist,int"/>
|
||||||
|
<var name="k5" type="set,int"/>
|
||||||
|
<var name="k6" type="treeset,int"/>
|
||||||
|
<var name="k7" type="hashset,int"/>
|
||||||
|
<var name="k8" type="map,int,int"/>
|
||||||
|
<var name="k9" type="list,DemoE2"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
|
||||||
|
<bean name="H1">
|
||||||
|
<var name="y2" type="H2"/>
|
||||||
|
<var name="y3" type="int"/>
|
||||||
|
</bean>
|
||||||
|
<bean name="H2">
|
||||||
|
<var name="z2" type="int"/>
|
||||||
|
<var name="z3" type="int"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean name="MultiRowTitle">
|
||||||
|
<var name="id" type="int"/>
|
||||||
|
<var name="name" type="string"/>
|
||||||
|
<var name="x1" type="H1"/>
|
||||||
|
<var name="x2" type="list,H2" multi_rows="1"/>
|
||||||
|
<var name="x3" type="array,H2" multi_rows="1"/>
|
||||||
|
</bean>
|
||||||
|
<table name="TbMultiRowTitle" value="MultiRowTitle" input="test/multi_level_title.xlsx"/>
|
||||||
|
|
||||||
|
<!--table name="TbDynamic" value="DemoDynamic" input="多态数据源"/-->
|
||||||
|
|
||||||
|
<module name="login">
|
||||||
|
支持在一个定义文件中 定义多个模块。 一般来说一个定义文件中一个模块比较好,但有些情况下为了方便可以定义多个。
|
||||||
|
<bean name="RoleInfo">
|
||||||
|
<var name="role_id" type="long"/>
|
||||||
|
</bean>
|
||||||
|
</module>
|
||||||
|
</module>
|
||||||
|
|
@ -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
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
**/bin/
|
||||||
|
**/obj/
|
||||||
|
|
@ -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
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="CommandLineParser" Version="2.8.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Luban.Common\Luban.Common.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,198 @@
|
||||||
|
using Bright.Net;
|
||||||
|
using Bright.Net.Bootstraps;
|
||||||
|
using Bright.Net.Channels;
|
||||||
|
using Bright.Net.Codecs;
|
||||||
|
using Bright.Net.ServiceModes.Managers;
|
||||||
|
using Bright.Time;
|
||||||
|
using Luban.Client.Common.Utils;
|
||||||
|
using Luban.Common.Protos;
|
||||||
|
using Luban.Common.Utils;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Luban.Client.Common.Net
|
||||||
|
{
|
||||||
|
public class Session : SessionBase
|
||||||
|
{
|
||||||
|
public override void OnActive()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnInactive()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GenClient : ClientManager<Session>
|
||||||
|
{
|
||||||
|
|
||||||
|
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
public static GenClient Ins { get; } = new GenClient();
|
||||||
|
|
||||||
|
public async Task Start(string host, int port, Dictionary<int, ProtocolCreator> factories)
|
||||||
|
{
|
||||||
|
var c = new TcpClientBootstrap
|
||||||
|
{
|
||||||
|
RemoteAddress = new IPEndPoint(IPAddress.Parse(host), port),
|
||||||
|
ConnectTimeoutMills = 100,
|
||||||
|
EventLoop = new EventLoop(null),
|
||||||
|
InitHandler = ch =>
|
||||||
|
{
|
||||||
|
ch.Pipeline.AddLast(new ProtocolFrameCodec(20_000_000, new RecycleByteBufPool(100, 10), new DefaultProtocolAllocator(factories)));
|
||||||
|
ch.Pipeline.AddLast(this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var ch = await c.ConnectAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void HandleProtocol(Protocol proto)
|
||||||
|
{
|
||||||
|
|
||||||
|
switch (proto.GetTypeId())
|
||||||
|
{
|
||||||
|
case GetInputFile.ID:
|
||||||
|
{
|
||||||
|
Task.Run(() => _ = OnGetInputFileAsync((GetInputFile)proto));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GetImportFileOrDirectory.ID:
|
||||||
|
{
|
||||||
|
Task.Run(() => _ = OnGetImportFileOrDirectoryAsync((GetImportFileOrDirectory)proto));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QueryFilesExists.ID:
|
||||||
|
{
|
||||||
|
Task.Run(() => Process((QueryFilesExists)proto));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PushLog.ID:
|
||||||
|
{
|
||||||
|
Process((PushLog)proto);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PushException.ID:
|
||||||
|
{
|
||||||
|
Process((PushException)proto);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
s_logger.Error("unknown proto:{proto}", proto);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnGetImportFileOrDirectoryAsync(GetImportFileOrDirectory rpc)
|
||||||
|
{
|
||||||
|
long t1 = TimeUtil.NowMillis;
|
||||||
|
var file = rpc.Arg.FileOrDirName;
|
||||||
|
var re = new GetImportFileOrDirectoryRes()
|
||||||
|
{
|
||||||
|
SubFiles = new List<Luban.Common.Protos.FileInfo>(),
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Directory.Exists(file))
|
||||||
|
{
|
||||||
|
re.Err = 0;
|
||||||
|
re.IsFile = false;
|
||||||
|
foreach (var subFile in Directory.GetFiles(file, "*", SearchOption.AllDirectories))
|
||||||
|
{
|
||||||
|
if (FileUtil.IsValidInputFile(subFile))
|
||||||
|
{
|
||||||
|
var md5 = await CacheMetaManager.Ins.GetOrUpdateFileMd5Async(subFile);
|
||||||
|
re.SubFiles.Add(new Luban.Common.Protos.FileInfo() { FilePath = FileUtil.Standardize(subFile), MD5 = md5 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (File.Exists(file))
|
||||||
|
{
|
||||||
|
re.IsFile = true;
|
||||||
|
re.Md5 = await CacheMetaManager.Ins.GetOrUpdateFileMd5Async(file);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
re.Err = Luban.Common.EErrorCode.FILE_OR_DIR_NOT_EXISTS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
re.Err = Luban.Common.EErrorCode.READ_FILE_FAIL;
|
||||||
|
s_logger.Error(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
s_logger.Trace(" GetImportFileOrDirectory file:{file} err:{err} cost:{time}", file, re.Err, TimeUtil.NowMillis - t1);
|
||||||
|
|
||||||
|
Session.ReplyRpc<GetImportFileOrDirectory, GetImportFileOrDirectoryArg, GetImportFileOrDirectoryRes>(rpc, re);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnGetInputFileAsync(GetInputFile rpc)
|
||||||
|
{
|
||||||
|
long t1 = TimeUtil.NowMillis;
|
||||||
|
var res = new GetInputFileRes() { Err = Luban.Common.EErrorCode.OK };
|
||||||
|
try
|
||||||
|
{
|
||||||
|
res.Content = await FileUtil.ReadAllBytesAsync(rpc.Arg.File);
|
||||||
|
//res.Content = FileUtil.ReadAllBytes(rpc.Arg.File);
|
||||||
|
res.Err = Luban.Common.EErrorCode.OK;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
res.Err = Luban.Common.EErrorCode.READ_FILE_FAIL;
|
||||||
|
}
|
||||||
|
s_logger.Info(" get input file:{file} err:{err} cost:{time}", rpc.Arg.File, res.Err, TimeUtil.NowMillis - t1);
|
||||||
|
|
||||||
|
Session.ReplyRpc<GetInputFile, GetInputFileArg, GetInputFileRes>(rpc, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Process(QueryFilesExists p)
|
||||||
|
{
|
||||||
|
var root = p.Arg.Root;
|
||||||
|
var files = p.Arg.Files;
|
||||||
|
var re = new QueryFilesExistsRes() { Exists = new List<bool>(files.Count) };
|
||||||
|
foreach (var f in files)
|
||||||
|
{
|
||||||
|
re.Exists.Add(File.Exists(Path.Combine(root, f)));
|
||||||
|
}
|
||||||
|
Session.ReplyRpc<QueryFilesExists, QueryFilesExistsArg, QueryFilesExistsRes>(p, re);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Process(PushLog p)
|
||||||
|
{
|
||||||
|
switch (p.Level)
|
||||||
|
{
|
||||||
|
case "trace":
|
||||||
|
{
|
||||||
|
s_logger.Trace(p.LogContent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "info":
|
||||||
|
{
|
||||||
|
s_logger.Info(p.LogContent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
s_logger.Error(p.LogContent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Process(PushException p)
|
||||||
|
{
|
||||||
|
s_logger.Error(p.LogContent);
|
||||||
|
s_logger.Error(p.StackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,217 @@
|
||||||
|
using Luban.Client.Common.Net;
|
||||||
|
using Luban.Client.Common.Utils;
|
||||||
|
using Luban.Common.Protos;
|
||||||
|
using Luban.Common.Utils;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Luban.Client
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
public class CommandLineOptions
|
||||||
|
{
|
||||||
|
public string Host { get; set; }
|
||||||
|
|
||||||
|
public int Port { get; set; } = 8899;
|
||||||
|
|
||||||
|
public string JobType { get; set; }
|
||||||
|
|
||||||
|
public List<string> JobArguments { get; set; } = new List<string>();
|
||||||
|
|
||||||
|
public bool Verbose { get; set; }
|
||||||
|
|
||||||
|
public string CacheMetaInfoFile { get; set; } = ".cache.meta";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NLog.Logger s_logger;
|
||||||
|
|
||||||
|
private static void PrintUsage(string err)
|
||||||
|
{
|
||||||
|
Console.WriteLine("ERRORS:");
|
||||||
|
Console.WriteLine("\t" + err);
|
||||||
|
Console.WriteLine(@"
|
||||||
|
Luban.Client <Options>... [-- [job options]]
|
||||||
|
e.g.
|
||||||
|
Luban.Client -h 127.0.0.1 -p 2234 -j cfg -- --name abc
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --host Required. host ip
|
||||||
|
-p --port port. default 8899
|
||||||
|
-j --job Required. job type. avaliable value: cfg
|
||||||
|
-v --verbose verbose print
|
||||||
|
-c --cachemetafile cache meta file name
|
||||||
|
-h --help show usage
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (object, CommandLineOptions) ParseArgs(string[] args)
|
||||||
|
{
|
||||||
|
var ops = new CommandLineOptions();
|
||||||
|
|
||||||
|
for (int i = 0; i < args.Length; i++)
|
||||||
|
{
|
||||||
|
var arg = args[i];
|
||||||
|
try
|
||||||
|
{
|
||||||
|
switch (arg)
|
||||||
|
{
|
||||||
|
case "-h":
|
||||||
|
case "--host":
|
||||||
|
{
|
||||||
|
|
||||||
|
ops.Host = args[++i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "-p":
|
||||||
|
case "--port":
|
||||||
|
{
|
||||||
|
ops.Port = int.Parse(args[++i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "-j":
|
||||||
|
case "--job":
|
||||||
|
{
|
||||||
|
ops.JobType = args[++i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "-v":
|
||||||
|
case "--verbose":
|
||||||
|
{
|
||||||
|
ops.Verbose = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "-c":
|
||||||
|
case "--cachemetafile":
|
||||||
|
{
|
||||||
|
ops.CacheMetaInfoFile = args[++i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "--":
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
ops.JobArguments = args.ToList().GetRange(i, args.Length - i);
|
||||||
|
return (null, ops);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
return ($"unknown argument:{arg}", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return ($"argument:{arg} err", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ops.Host == null)
|
||||||
|
{
|
||||||
|
return ("--host missing", null);
|
||||||
|
}
|
||||||
|
if (ops.JobType == null)
|
||||||
|
{
|
||||||
|
return ("--job missing", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null, ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
var profile = new ProfileTimer();
|
||||||
|
|
||||||
|
profile.StartPhase("all");
|
||||||
|
|
||||||
|
var parseResult = ParseArgs(args);
|
||||||
|
if (parseResult.Item1 != null)
|
||||||
|
{
|
||||||
|
PrintUsage((string)parseResult.Item1);
|
||||||
|
Environment.Exit(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CommandLineOptions options = parseResult.Item2;
|
||||||
|
|
||||||
|
profile.StartPhase("init logger");
|
||||||
|
|
||||||
|
LogUtil.InitSimpleNLogConfigure(NLog.LogLevel.Info);
|
||||||
|
s_logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
profile.EndPhaseAndLog();
|
||||||
|
|
||||||
|
ThreadPool.SetMinThreads(4, 5);
|
||||||
|
ThreadPool.SetMaxThreads(64, 10);
|
||||||
|
|
||||||
|
int exitCode;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
profile.StartPhase("load cache meta file");
|
||||||
|
CacheMetaManager.Ins.Load(options.CacheMetaInfoFile);
|
||||||
|
profile.EndPhaseAndLog();
|
||||||
|
profile.StartPhase("connect server");
|
||||||
|
var conn = GenClient.Ins.Start(options.Host, options.Port, ProtocolStub.Factories);
|
||||||
|
conn.Wait();
|
||||||
|
profile.EndPhaseAndLog();
|
||||||
|
|
||||||
|
profile.StartPhase("gen job");
|
||||||
|
exitCode = SubmitGenJob(options);
|
||||||
|
profile.EndPhaseAndLog();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
exitCode = 1;
|
||||||
|
s_logger.Error(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
CacheMetaManager.Ins.Save();
|
||||||
|
profile.EndPhaseAndLog();
|
||||||
|
if (exitCode == 0)
|
||||||
|
{
|
||||||
|
s_logger.Info("== succ ==");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s_logger.Error("== fail ==");
|
||||||
|
}
|
||||||
|
Environment.Exit(exitCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int GEN_JOB_TIMEOUT = 30;
|
||||||
|
|
||||||
|
private static int SubmitGenJob(CommandLineOptions options)
|
||||||
|
{
|
||||||
|
var res = GenClient.Ins.Session.CallRpcAsync<GenJob, GenJobArg, GenJobRes>(new GenJobArg()
|
||||||
|
{
|
||||||
|
JobType = options.JobType,
|
||||||
|
JobArguments = options.JobArguments,
|
||||||
|
Verbose = options.Verbose,
|
||||||
|
}, GEN_JOB_TIMEOUT).Result;
|
||||||
|
|
||||||
|
if (res.ErrCode != 0)
|
||||||
|
{
|
||||||
|
if (res.ErrCode == Luban.Common.EErrorCode.JOB_ARGUMENT_ERROR)
|
||||||
|
{
|
||||||
|
s_logger.Error("job argument error");
|
||||||
|
Console.WriteLine(res.ErrMsg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s_logger.Error("GenJob fail. err:{err} msg:{msg}", res.ErrCode, res.ErrMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tasks = new List<Task>();
|
||||||
|
|
||||||
|
foreach (var fg in res.FileGroups)
|
||||||
|
{
|
||||||
|
tasks.Add(DownloadFileUtil.DownloadGeneratedFiles(fg.Dir, fg.Files));
|
||||||
|
}
|
||||||
|
|
||||||
|
Task.WaitAll(tasks.ToArray());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,213 @@
|
||||||
|
using Luban.Common.Utils;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Luban.Client.Common.Utils
|
||||||
|
{
|
||||||
|
public class CacheMetaManager
|
||||||
|
{
|
||||||
|
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
public static CacheMetaManager Ins { get; } = new CacheMetaManager();
|
||||||
|
|
||||||
|
|
||||||
|
class MetaInfo
|
||||||
|
{
|
||||||
|
public string FullPath { get; set; }
|
||||||
|
|
||||||
|
public string Md5 { get; set; }
|
||||||
|
|
||||||
|
public long FileLength { get; set; }
|
||||||
|
|
||||||
|
public long FileModifiedTime { get; set; }
|
||||||
|
|
||||||
|
public bool Visited { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly object _lock = new object();
|
||||||
|
|
||||||
|
private string _metaFile;
|
||||||
|
private volatile bool _dirty;
|
||||||
|
private readonly SortedDictionary<string, MetaInfo> _cacheFileMetas = new SortedDictionary<string, MetaInfo>();
|
||||||
|
|
||||||
|
public void Load(string metaFile)
|
||||||
|
{
|
||||||
|
_metaFile = metaFile;
|
||||||
|
_dirty = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (File.Exists(metaFile))
|
||||||
|
{
|
||||||
|
foreach (string line in File.ReadAllLines(metaFile, Encoding.UTF8))
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(line))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
string[] args = line.Split(',');
|
||||||
|
if (args.Length != 4)
|
||||||
|
{
|
||||||
|
_dirty = true;
|
||||||
|
s_logger.Error("corrupt line:{line}", line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var fullPath = args[0];
|
||||||
|
string md5 = args[1];
|
||||||
|
long fileLength = long.Parse(args[2]);
|
||||||
|
long fileModifiedTime = long.Parse(args[3]);
|
||||||
|
if (!File.Exists(fullPath))
|
||||||
|
{
|
||||||
|
_dirty = true;
|
||||||
|
s_logger.Debug("[drop] cache file:{file} not exist.", fullPath);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileInfo = new FileInfo(fullPath);
|
||||||
|
|
||||||
|
long actualFileLength = fileInfo.Length;
|
||||||
|
if (actualFileLength != fileLength)
|
||||||
|
{
|
||||||
|
_dirty = true;
|
||||||
|
s_logger.Debug("[drop] cache file:{file} length not match. cache length:{cl} actual length:{al}", fullPath, fileLength, actualFileLength);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
long actualLastModifiedTime = ((DateTimeOffset)fileInfo.LastWriteTime).ToUnixTimeMilliseconds();
|
||||||
|
if (actualLastModifiedTime != fileModifiedTime)
|
||||||
|
{
|
||||||
|
_dirty = true;
|
||||||
|
s_logger.Debug("[drop] cache file:{file} last modified time not match. cache:{cl} actual:{al}", fullPath, fileModifiedTime, actualLastModifiedTime);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_cacheFileMetas[fullPath] = new MetaInfo
|
||||||
|
{
|
||||||
|
FullPath = fullPath,
|
||||||
|
Md5 = md5,
|
||||||
|
FileLength = fileLength,
|
||||||
|
FileModifiedTime = fileModifiedTime,
|
||||||
|
};
|
||||||
|
s_logger.Debug("load cache. file:{file} md5:{md5} length:{length} last modified time:{time}", fullPath, md5, fileLength, fileModifiedTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s_logger.Info("meta file:{meta} not exist. ignore load", metaFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
s_logger.Error(e, "load meta file fail");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
if (!_dirty)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_dirty = false;
|
||||||
|
var content = _cacheFileMetas.Values.Select(meta => $"{meta.FullPath},{meta.Md5},{meta.FileLength},{meta.FileModifiedTime}");
|
||||||
|
File.WriteAllLines(_metaFile, content, Encoding.UTF8);
|
||||||
|
}
|
||||||
|
s_logger.Info("[Save] meta file:{metaFile} updated!", _metaFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MetaInfo GetMetaInfo(string file)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
var fullPath = Path.GetFullPath(file).Replace('\\', '/');
|
||||||
|
return _cacheFileMetas.TryGetValue(fullPath, out var meta) ? meta : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<MetaInfo> BuildMetaInfo(string file, string md5 = null)
|
||||||
|
{
|
||||||
|
var fullPath = Path.GetFullPath(file).Replace('\\', '/');
|
||||||
|
if (md5 == null)
|
||||||
|
{
|
||||||
|
s_logger.Info("comput md5. file:{file}", file);
|
||||||
|
md5 = FileUtil.CalcMD5(await FileUtil.ReadAllBytesAsync(file));
|
||||||
|
}
|
||||||
|
var fileInfo = new FileInfo(fullPath);
|
||||||
|
long actualFileLength = fileInfo.Length;
|
||||||
|
long actualLastModifiedTime = ((DateTimeOffset)fileInfo.LastWriteTime).ToUnixTimeMilliseconds();
|
||||||
|
return new MetaInfo()
|
||||||
|
{
|
||||||
|
FullPath = fullPath,
|
||||||
|
Md5 = md5,
|
||||||
|
FileLength = actualFileLength,
|
||||||
|
FileModifiedTime = actualLastModifiedTime,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> GetOrUpdateFileMd5Async(string file)
|
||||||
|
{
|
||||||
|
var meta = GetMetaInfo(file);
|
||||||
|
|
||||||
|
if (meta == null)
|
||||||
|
{
|
||||||
|
meta = await BuildMetaInfo(file);
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_dirty = true;
|
||||||
|
_cacheFileMetas.Add(meta.FullPath, meta);
|
||||||
|
}
|
||||||
|
s_logger.Debug("[add] meta not find, build it. file:{file} path:{path} md5:{md5} length:{length}", file, meta.FullPath, meta.Md5, meta.FileLength);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s_logger.Debug("[cache hit] file:{file} path:{path} md5:{md5} length:{length}", file, meta.FullPath, meta.Md5, meta.FileLength);
|
||||||
|
}
|
||||||
|
return meta.Md5;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> CheckFileChangeAsync(string relateDir, string filePath, string md5)
|
||||||
|
{
|
||||||
|
var outputPath = relateDir != null ? FileUtil.Combine(relateDir, filePath) : filePath;
|
||||||
|
|
||||||
|
var meta = GetMetaInfo(outputPath);
|
||||||
|
if (meta == null)
|
||||||
|
{
|
||||||
|
if (!File.Exists(outputPath))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
meta = await BuildMetaInfo(outputPath);
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_dirty = true;
|
||||||
|
_cacheFileMetas.Add(meta.FullPath, meta);
|
||||||
|
}
|
||||||
|
s_logger.Debug("[add] meta not find, create it. file:{file} path:{path} md5:{md5} length:{length}", outputPath, meta.FullPath, meta.Md5, meta.FileLength);
|
||||||
|
}
|
||||||
|
if (meta.Md5 != md5)
|
||||||
|
{
|
||||||
|
s_logger.Debug("[add] meta md5 not match, file:{file} path:{path} md5:{md5} length:{length}", outputPath, meta.FullPath, meta.Md5, meta.FileLength);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateFileAsync(string relateDir, string filePath, string md5)
|
||||||
|
{
|
||||||
|
var file = relateDir != null ? FileUtil.Combine(relateDir, filePath) : filePath;
|
||||||
|
var meta = await BuildMetaInfo(file, md5);
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_dirty = true;
|
||||||
|
_cacheFileMetas[meta.FullPath] = meta;
|
||||||
|
}
|
||||||
|
s_logger.Debug("[update] file:{file} path:{path} md5:{md5} length:{length}", file, meta.FullPath, meta.Md5, meta.FileLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
using Luban.Client.Common.Net;
|
||||||
|
using Luban.Common.Protos;
|
||||||
|
using Luban.Common.Utils;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Luban.Client.Common.Utils
|
||||||
|
{
|
||||||
|
public static class DownloadFileUtil
|
||||||
|
{
|
||||||
|
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
const int DOWNLOAD_TIMTOUT = 10;
|
||||||
|
|
||||||
|
public static async Task DownloadGeneratedFiles(string outputDir, List<FileInfo> newFiles)
|
||||||
|
{
|
||||||
|
List<Task> tasks = new List<Task>();
|
||||||
|
foreach (var file in newFiles)
|
||||||
|
{
|
||||||
|
if (!await CacheMetaManager.Ins.CheckFileChangeAsync(outputDir, file.FilePath, file.MD5))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
tasks.Add(Task.Run(async () =>
|
||||||
|
{
|
||||||
|
s_logger.Trace("new code file:{@file}", file);
|
||||||
|
GetOutputFileRes res = await GenClient.Ins.Session.CallRpcAsync<GetOutputFile, GetOutputFileArg, GetOutputFileRes>(new GetOutputFileArg()
|
||||||
|
{
|
||||||
|
MD5 = file.MD5,
|
||||||
|
}, DOWNLOAD_TIMTOUT);
|
||||||
|
|
||||||
|
await FileUtil.SaveFileAsync(outputDir, file.FilePath, res.FileContent);
|
||||||
|
await CacheMetaManager.Ins.UpdateFileAsync(outputDir, file.FilePath, file.MD5);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
await Task.WhenAll(tasks);
|
||||||
|
|
||||||
|
// todo 感觉有点问题哈,不是每个生成目录都需要clean up 的
|
||||||
|
FileCleaner.Clean(outputDir, newFiles);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task DownloadGeneratedFile(FileInfo file)
|
||||||
|
{
|
||||||
|
if (!await CacheMetaManager.Ins.CheckFileChangeAsync(null, file.FilePath, file.MD5))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GetOutputFileRes res = await GenClient.Ins.Session.CallRpcAsync<GetOutputFile, GetOutputFileArg, GetOutputFileRes>(new GetOutputFileArg()
|
||||||
|
{
|
||||||
|
MD5 = file.MD5,
|
||||||
|
}, DOWNLOAD_TIMTOUT).ConfigureAwait(false);
|
||||||
|
|
||||||
|
await FileUtil.SaveFileAsync(null, file.FilePath, res.FileContent);
|
||||||
|
await CacheMetaManager.Ins.UpdateFileAsync(null, file.FilePath, file.MD5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Luban.Client.Common.Utils
|
||||||
|
{
|
||||||
|
class FileCleaner
|
||||||
|
{
|
||||||
|
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
private readonly HashSet<string> _outputDirs = new HashSet<string>();
|
||||||
|
private readonly HashSet<string> _savedFileOrDirs = new HashSet<string>();
|
||||||
|
private readonly HashSet<string> _ignoreFileExtensions = new HashSet<string>();
|
||||||
|
|
||||||
|
|
||||||
|
public void AddIgnoreExtension(string ext)
|
||||||
|
{
|
||||||
|
_ignoreFileExtensions.Add(ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddOutputDir(string dir)
|
||||||
|
{
|
||||||
|
_outputDirs.Add(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddSavedFile(string file)
|
||||||
|
{
|
||||||
|
file = file.Replace('\\', '/');
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
_savedFileOrDirs.Add(file);
|
||||||
|
s_logger.Trace("add save file:{file}", file);
|
||||||
|
int sepIndex = file.LastIndexOf('/');
|
||||||
|
if (sepIndex < 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
file = file[..sepIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void RemoveUnusedFiles()
|
||||||
|
{
|
||||||
|
foreach (var dir in _outputDirs)
|
||||||
|
{
|
||||||
|
RemoveUnusedFileInDir(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveUnusedFileInDir(string dir)
|
||||||
|
{
|
||||||
|
if (!Directory.Exists(dir))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fullRootPath = Path.GetFullPath(dir);
|
||||||
|
s_logger.Trace("full path:{path}", fullRootPath);
|
||||||
|
|
||||||
|
foreach (var file in Directory.GetFiles(dir, "*", SearchOption.AllDirectories))
|
||||||
|
{
|
||||||
|
s_logger.Trace("file:{file}", file);
|
||||||
|
string fullSubFilePath = Path.GetFullPath(file);
|
||||||
|
var relateFile = fullSubFilePath[(fullRootPath.Length + 1)..].Replace('\\', '/');
|
||||||
|
if (_savedFileOrDirs.Contains(relateFile))
|
||||||
|
{
|
||||||
|
s_logger.Trace("remain file:{file}", file);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s_logger.Info("[remove] file: {file}", file);
|
||||||
|
File.Delete(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除空目录
|
||||||
|
var subDirs = new List<string>(Directory.GetDirectories(dir, "*", SearchOption.AllDirectories));
|
||||||
|
subDirs.Sort((a, b) => -a.CompareTo(b));
|
||||||
|
foreach (var subDir in subDirs)
|
||||||
|
{
|
||||||
|
string fullSubDirPath = Path.GetFullPath(subDir);
|
||||||
|
var relateDir = fullSubDirPath[(fullRootPath.Length + 1)..].Replace('\\', '/');
|
||||||
|
if (_savedFileOrDirs.Contains(relateDir))
|
||||||
|
{
|
||||||
|
s_logger.Trace("remain directory:{dir}", relateDir);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s_logger.Info("[remove] dir: {dir}", subDir);
|
||||||
|
Directory.Delete(subDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void Clean(string outputDir, List<Luban.Common.Protos.FileInfo> savedFiles)
|
||||||
|
{
|
||||||
|
var cleaner = new FileCleaner();
|
||||||
|
cleaner.AddOutputDir(outputDir);
|
||||||
|
cleaner.AddIgnoreExtension("meta"); // for unity
|
||||||
|
foreach (var file in savedFiles)
|
||||||
|
{
|
||||||
|
cleaner.AddSavedFile(file.FilePath);
|
||||||
|
}
|
||||||
|
cleaner.RemoveUnusedFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Bright.Net" Version="1.1.0.41" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
using Bright.Net.Codecs;
|
||||||
|
using Bright.Serialization;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Luban.Common.Protos
|
||||||
|
{
|
||||||
|
public class GenJobArg : BeanBase
|
||||||
|
{
|
||||||
|
public string JobType { get; set; }
|
||||||
|
|
||||||
|
public List<string> JobArguments { get; set; }
|
||||||
|
|
||||||
|
public bool Verbose { get; set; }
|
||||||
|
|
||||||
|
public override int GetTypeId()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Serialize(ByteBuf os)
|
||||||
|
{
|
||||||
|
os.WriteString(JobType);
|
||||||
|
Bright.Common.SerializationUtil.Serialize(os, JobArguments);
|
||||||
|
os.WriteBool(Verbose);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Deserialize(ByteBuf os)
|
||||||
|
{
|
||||||
|
JobType = os.ReadString();
|
||||||
|
Bright.Common.SerializationUtil.Deserialize(os, JobArguments = new List<string>());
|
||||||
|
Verbose = os.ReadBool();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class FileGroup : BeanBase
|
||||||
|
{
|
||||||
|
public string Dir { get; set; }
|
||||||
|
|
||||||
|
public List<FileInfo> Files { get; set; }
|
||||||
|
|
||||||
|
public override int GetTypeId()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Serialize(ByteBuf os)
|
||||||
|
{
|
||||||
|
os.WriteString(Dir);
|
||||||
|
Bright.Common.SerializationUtil.Serialize(os, Files);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Deserialize(ByteBuf os)
|
||||||
|
{
|
||||||
|
Dir = os.ReadString();
|
||||||
|
Bright.Common.SerializationUtil.Deserialize(os, Files = new List<FileInfo>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class GenJobRes : BeanBase
|
||||||
|
{
|
||||||
|
public EErrorCode ErrCode { get; set; }
|
||||||
|
|
||||||
|
public string ErrMsg { get; set; }
|
||||||
|
|
||||||
|
public List<FileGroup> FileGroups { get; set; }
|
||||||
|
|
||||||
|
public override int GetTypeId()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Serialize(ByteBuf os)
|
||||||
|
{
|
||||||
|
os.WriteInt((int)ErrCode);
|
||||||
|
os.WriteString(ErrMsg);
|
||||||
|
Bright.Common.SerializationUtil.Serialize(os, FileGroups);
|
||||||
|
}
|
||||||
|
public override void Deserialize(ByteBuf os)
|
||||||
|
{
|
||||||
|
ErrCode = (EErrorCode)os.ReadInt();
|
||||||
|
ErrMsg = os.ReadString();
|
||||||
|
Bright.Common.SerializationUtil.Deserialize(os, FileGroups = new List<FileGroup>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GenJob : Rpc<GenJobArg, GenJobRes>
|
||||||
|
{
|
||||||
|
public const int ID = 100;
|
||||||
|
|
||||||
|
public override int GetTypeId()
|
||||||
|
{
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object Clone()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Reset()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
using Bright.Net.Codecs;
|
||||||
|
using Bright.Serialization;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Luban.Common.Protos
|
||||||
|
{
|
||||||
|
public class GetImportFileOrDirectoryArg : BeanBase
|
||||||
|
{
|
||||||
|
public string FileOrDirName { get; set; }
|
||||||
|
|
||||||
|
public override int GetTypeId()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Serialize(ByteBuf os)
|
||||||
|
{
|
||||||
|
os.WriteString(FileOrDirName);
|
||||||
|
}
|
||||||
|
public override void Deserialize(ByteBuf os)
|
||||||
|
{
|
||||||
|
FileOrDirName = os.ReadString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GetImportFileOrDirectoryRes : BeanBase
|
||||||
|
{
|
||||||
|
public EErrorCode Err { get; set; }
|
||||||
|
|
||||||
|
public bool IsFile { get; set; }
|
||||||
|
|
||||||
|
public string Md5 { get; set; }
|
||||||
|
|
||||||
|
//public byte[] Content { get; set; }
|
||||||
|
|
||||||
|
public List<FileInfo> SubFiles { get; set; }
|
||||||
|
|
||||||
|
public override int GetTypeId()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Serialize(ByteBuf os)
|
||||||
|
{
|
||||||
|
os.WriteInt((int)Err);
|
||||||
|
os.WriteBool(IsFile);
|
||||||
|
os.WriteString(Md5);
|
||||||
|
Bright.Common.SerializationUtil.Serialize(os, SubFiles);
|
||||||
|
}
|
||||||
|
public override void Deserialize(ByteBuf os)
|
||||||
|
{
|
||||||
|
Err = (EErrorCode)os.ReadInt();
|
||||||
|
IsFile = os.ReadBool();
|
||||||
|
Md5 = os.ReadString();
|
||||||
|
Bright.Common.SerializationUtil.Deserialize(os, SubFiles = new List<FileInfo>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GetImportFileOrDirectory : Rpc<GetImportFileOrDirectoryArg, GetImportFileOrDirectoryRes>
|
||||||
|
{
|
||||||
|
public const int ID = 108;
|
||||||
|
|
||||||
|
public override int GetTypeId()
|
||||||
|
{
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Reset()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object Clone()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
using Bright.Net.Codecs;
|
||||||
|
using Bright.Serialization;
|
||||||
|
|
||||||
|
namespace Luban.Common.Protos
|
||||||
|
{
|
||||||
|
public class GetInputFileArg : BeanBase
|
||||||
|
{
|
||||||
|
public string File { get; set; }
|
||||||
|
|
||||||
|
public override void Serialize(ByteBuf os)
|
||||||
|
{
|
||||||
|
os.WriteString(File);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Deserialize(ByteBuf os)
|
||||||
|
{
|
||||||
|
File = os.ReadString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetTypeId()
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class GetInputFileRes : BeanBase
|
||||||
|
{
|
||||||
|
public EErrorCode Err { get; set; }
|
||||||
|
|
||||||
|
public byte[] Content { get; set; }
|
||||||
|
|
||||||
|
public override void Serialize(ByteBuf os)
|
||||||
|
{
|
||||||
|
os.WriteInt((int)Err);
|
||||||
|
os.WriteBytes(Content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Deserialize(ByteBuf os)
|
||||||
|
{
|
||||||
|
Err = (EErrorCode)os.ReadInt();
|
||||||
|
Content = os.ReadBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetTypeId()
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GetInputFile : Rpc<GetInputFileArg, GetInputFileRes>
|
||||||
|
{
|
||||||
|
public const int ID = 102;
|
||||||
|
|
||||||
|
public override int GetTypeId()
|
||||||
|
{
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Reset()
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object Clone()
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
using Bright.Net.Codecs;
|
||||||
|
using Bright.Serialization;
|
||||||
|
|
||||||
|
namespace Luban.Common.Protos
|
||||||
|
{
|
||||||
|
|
||||||
|
public class GetOutputFile : Rpc<GetOutputFileArg, GetOutputFileRes>
|
||||||
|
{
|
||||||
|
public const int ID = 103;
|
||||||
|
|
||||||
|
public override int GetTypeId()
|
||||||
|
{
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Reset()
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object Clone()
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class GetOutputFileArg : BeanBase
|
||||||
|
{
|
||||||
|
//public string Type { get; set; }
|
||||||
|
|
||||||
|
//public string RelatePath { get; set; }
|
||||||
|
|
||||||
|
public string MD5 { get; set; }
|
||||||
|
|
||||||
|
public override int GetTypeId()
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Serialize(ByteBuf os)
|
||||||
|
{
|
||||||
|
//os.WriteString(Type);
|
||||||
|
//os.WriteString(RelatePath);
|
||||||
|
os.WriteString(MD5);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Deserialize(ByteBuf os)
|
||||||
|
{
|
||||||
|
//Type = os.ReadString();
|
||||||
|
//RelatePath = os.ReadString();
|
||||||
|
MD5 = os.ReadString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class GetOutputFileRes : BeanBase
|
||||||
|
{
|
||||||
|
public bool Exists { get; set; }
|
||||||
|
public byte[] FileContent { get; set; }
|
||||||
|
|
||||||
|
public override void Serialize(ByteBuf os)
|
||||||
|
{
|
||||||
|
os.WriteBool(Exists);
|
||||||
|
os.WriteBytes(FileContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Deserialize(ByteBuf os)
|
||||||
|
{
|
||||||
|
Exists = os.ReadBool();
|
||||||
|
FileContent = os.ReadBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetTypeId()
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
using Bright.Net.Codecs;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Luban.Common.Protos
|
||||||
|
{
|
||||||
|
public static class ProtocolStub
|
||||||
|
{
|
||||||
|
public static Dictionary<int, ProtocolCreator> Factories { get; } = new Dictionary<int, ProtocolCreator>
|
||||||
|
{
|
||||||
|
[GetInputFile.ID] = () => new GetInputFile(),
|
||||||
|
[GetOutputFile.ID] = () => new GetOutputFile(),
|
||||||
|
[PushLog.ID] = () => new PushLog(),
|
||||||
|
[PushException.ID] = () => new PushException(),
|
||||||
|
[GenJob.ID] = () => new GenJob(),
|
||||||
|
[GetImportFileOrDirectory.ID] = () => new GetImportFileOrDirectory(),
|
||||||
|
[QueryFilesExists.ID] = () => new QueryFilesExists(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
using Bright.Net.Codecs;
|
||||||
|
using Bright.Serialization;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Luban.Common.Protos
|
||||||
|
{
|
||||||
|
public class QueryFilesExistsArg : BeanBase
|
||||||
|
{
|
||||||
|
public string Root { get; set; }
|
||||||
|
|
||||||
|
public List<string> Files { get; set; }
|
||||||
|
|
||||||
|
public override int GetTypeId()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Serialize(ByteBuf os)
|
||||||
|
{
|
||||||
|
os.WriteString(Root);
|
||||||
|
Bright.Common.SerializationUtil.Serialize(os, Files);
|
||||||
|
}
|
||||||
|
public override void Deserialize(ByteBuf os)
|
||||||
|
{
|
||||||
|
Root = os.ReadString();
|
||||||
|
Bright.Common.SerializationUtil.Deserialize(os, Files = new List<string>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class QueryFilesExistsRes : BeanBase
|
||||||
|
{
|
||||||
|
public List<bool> Exists { get; set; }
|
||||||
|
|
||||||
|
public override int GetTypeId()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Serialize(ByteBuf os)
|
||||||
|
{
|
||||||
|
os.WriteSize(Exists.Count);
|
||||||
|
foreach (var v in Exists)
|
||||||
|
{
|
||||||
|
os.WriteBool(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override void Deserialize(ByteBuf os)
|
||||||
|
{
|
||||||
|
int n = os.ReadSize();
|
||||||
|
Exists = new List<bool>();
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
Exists.Add(os.ReadBool());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class QueryFilesExists : Rpc<QueryFilesExistsArg, QueryFilesExistsRes>
|
||||||
|
{
|
||||||
|
public const int ID = 106;
|
||||||
|
|
||||||
|
public override int GetTypeId()
|
||||||
|
{
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object Clone()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Reset()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,145 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Luban.Common.Utils
|
||||||
|
{
|
||||||
|
public static class FileUtil
|
||||||
|
{
|
||||||
|
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
public static string GetFileName(string path)
|
||||||
|
{
|
||||||
|
int index = path.Replace('\\', '/').LastIndexOf('/');
|
||||||
|
return index >= 0 ? path[(index + 1)..] : path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetParent(string path)
|
||||||
|
{
|
||||||
|
int index = path.Replace('\\', '/').LastIndexOf('/');
|
||||||
|
return index >= 0 ? path[..index] : path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetPathRelateRootFile(string rootFile, string file)
|
||||||
|
{
|
||||||
|
return Combine(GetParent(rootFile), file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 忽略以 文件名以 '.' '_' '~' 开头的文件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="file"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool IsValidInputFile(string file)
|
||||||
|
{
|
||||||
|
if (!File.Exists(file))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var f = new FileInfo(file);
|
||||||
|
string fname = f.Name;
|
||||||
|
return !fname.StartsWith('.') && !fname.StartsWith('_') && !fname.StartsWith('~');
|
||||||
|
}
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static MD5 s_cacheMd5;
|
||||||
|
|
||||||
|
private static MD5 CacheMd5
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var md5 = s_cacheMd5 ??= MD5.Create();
|
||||||
|
md5.Clear();
|
||||||
|
return md5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string CalcMD5(byte[] srcBytes)
|
||||||
|
{
|
||||||
|
using MD5 md5 = MD5.Create();
|
||||||
|
var md5Bytes = md5.ComputeHash(srcBytes);
|
||||||
|
var s = new StringBuilder(md5Bytes.Length * 2);
|
||||||
|
foreach (var b in md5Bytes)
|
||||||
|
{
|
||||||
|
s.Append(b.ToString("X"));
|
||||||
|
}
|
||||||
|
return s.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Standardize(string path)
|
||||||
|
{
|
||||||
|
return path.Replace('\\', '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Combine(string parent, string sub)
|
||||||
|
{
|
||||||
|
return Standardize(Path.Combine(parent, sub));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task SaveFileAsync(string relateDir, string filePath, byte[] content)
|
||||||
|
{
|
||||||
|
// 调用此接口时,已保证 文件必然是改变的,不用再检查对比文件
|
||||||
|
var outputPath = Standardize(relateDir != null ? System.IO.Path.Combine(relateDir, filePath) : filePath);
|
||||||
|
Directory.GetParent(outputPath).Create();
|
||||||
|
if (File.Exists(outputPath))
|
||||||
|
{
|
||||||
|
//if (CheckFileNotChange(outputPath, content))
|
||||||
|
//{
|
||||||
|
// s_logger.Trace("[not change] {file}", outputPath);
|
||||||
|
// return;
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// s_logger.Info("[override] {file}", outputPath);
|
||||||
|
// if (File.GetAttributes(outputPath).HasFlag(FileAttributes.ReadOnly))
|
||||||
|
// {
|
||||||
|
// File.SetAttributes(outputPath, FileAttributes.Normal);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
s_logger.Info("[override] {file}", outputPath);
|
||||||
|
if (File.GetAttributes(outputPath).HasFlag(FileAttributes.ReadOnly))
|
||||||
|
{
|
||||||
|
File.SetAttributes(outputPath, FileAttributes.Normal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s_logger.Info("[new] {file}", outputPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
await File.WriteAllBytesAsync(outputPath, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<byte[]> ReadAllBytesAsync(string file)
|
||||||
|
{
|
||||||
|
// File.ReadAllBytesAsync 无法读取被打开的excel文件,只好重新实现了一个
|
||||||
|
using var fs = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||||
|
long count = fs.Length;
|
||||||
|
var bytes = new byte[count];
|
||||||
|
int writeIndex = 0;
|
||||||
|
while (writeIndex < count)
|
||||||
|
{
|
||||||
|
int n = await fs.ReadAsync(bytes, writeIndex, (int)count - writeIndex, default);
|
||||||
|
writeIndex += n;
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] ReadAllBytes(string file)
|
||||||
|
{
|
||||||
|
// File.ReadAllBytesAsync 无法读取被打开的excel文件,只好重新实现了一个
|
||||||
|
using var fs = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||||
|
long count = fs.Length;
|
||||||
|
var bytes = new byte[count];
|
||||||
|
int writeIndex = 0;
|
||||||
|
while (writeIndex < count)
|
||||||
|
{
|
||||||
|
int n = fs.Read(bytes, writeIndex, (int)count - writeIndex);
|
||||||
|
writeIndex += n;
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
using Bright.Time;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Luban.Common.Utils
|
||||||
|
{
|
||||||
|
public class Phase
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public long StartTime { get; set; }
|
||||||
|
|
||||||
|
public long EndTime { get; set; }
|
||||||
|
|
||||||
|
public long ElapseTime { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ProfileTimer
|
||||||
|
{
|
||||||
|
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
private readonly Stack<Phase> phaseStack = new Stack<Phase>();
|
||||||
|
|
||||||
|
public void StartPhase(string name)
|
||||||
|
{
|
||||||
|
phaseStack.Push(new Phase() { Name = name, StartTime = TimeUtil.NowMillis });
|
||||||
|
}
|
||||||
|
|
||||||
|
private Phase EndPhase()
|
||||||
|
{
|
||||||
|
var phase = phaseStack.Pop();
|
||||||
|
phase.EndTime = TimeUtil.NowMillis;
|
||||||
|
phase.ElapseTime = phase.EndTime - phase.StartTime;
|
||||||
|
return phase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Phase EndPhaseAndLog()
|
||||||
|
{
|
||||||
|
var phase = EndPhase();
|
||||||
|
s_logger.Info("====== {name} cost {time} ms ======", phase.Name, phase.ElapseTime);
|
||||||
|
return phase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,207 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Luban.Common.Utils
|
||||||
|
{
|
||||||
|
public static class TypeUtil
|
||||||
|
{
|
||||||
|
|
||||||
|
public static (string, string) SplitFullName(string fullName)
|
||||||
|
{
|
||||||
|
int index = fullName.LastIndexOf('.');
|
||||||
|
return (fullName.Substring(0, index), fullName.Substring(index + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string MakeFullName(Stack<object> path)
|
||||||
|
{
|
||||||
|
var reverse = new List<string>();
|
||||||
|
int index = 0;
|
||||||
|
foreach (var e in path.Reverse())
|
||||||
|
{
|
||||||
|
if (!(e is string))
|
||||||
|
{
|
||||||
|
reverse.Add("[" + e + "]");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 索引 0 是 table名, 不应该加 .
|
||||||
|
if (index >= 1)
|
||||||
|
{
|
||||||
|
reverse.Add(".");
|
||||||
|
}
|
||||||
|
reverse.Add(e.ToString());
|
||||||
|
}
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
return string.Join("", reverse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string MakeCppNamespaceBegin(string module)
|
||||||
|
{
|
||||||
|
return string.Join("", module.Split('.').Select(n => $"namespace {n} {{"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string MakeCppNamespaceEnd(string module)
|
||||||
|
{
|
||||||
|
return string.Join("", module.Split('.').Select(n => $"}}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string MakeCppFullName(string module, string name)
|
||||||
|
{
|
||||||
|
return string.Join("::", MakeFullName(module, name).Split('.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string MakeCppJoinedFullName(string module, string ueName)
|
||||||
|
{
|
||||||
|
return string.Join("", module.Split('.').Select(n => $"{n}_")) + ueName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string MakeFullName(string module, string name)
|
||||||
|
{
|
||||||
|
return module != null && module.Length > 0 ? module + "." + name : name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string MakeGoPkgName(string module)
|
||||||
|
{
|
||||||
|
int index = module.LastIndexOf('.');
|
||||||
|
return index >= 0 ? module.Substring(index + 1) : module;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string MakeGoNamespace(string module)
|
||||||
|
{
|
||||||
|
return string.Join("", module.Split('.').Where(s => !string.IsNullOrWhiteSpace(s)).Select(s => UpperCaseFirstChar(s)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string MakeGoFullName(string module, string name)
|
||||||
|
{
|
||||||
|
return MakeGoNamespace(module) + "_" + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string MakePyFullName(string module, string name)
|
||||||
|
{
|
||||||
|
return module.Replace('.', '_') + "_" + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string MakeNamespace(string module, string subModule)
|
||||||
|
{
|
||||||
|
if (module.Length == 0)
|
||||||
|
{
|
||||||
|
return subModule;
|
||||||
|
}
|
||||||
|
if (subModule.Length == 0)
|
||||||
|
{
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
return module + "." + subModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// the return value in range [offset, 2^14)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static uint ComputProtoHashIdByName(string name)
|
||||||
|
{
|
||||||
|
uint id = 0;
|
||||||
|
foreach (char c in name)
|
||||||
|
{
|
||||||
|
id = 31 * id + c;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint maxId = 2 << 14;
|
||||||
|
uint offset = 1000;
|
||||||
|
return id % (maxId - offset) + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int ComputCfgHashIdByName(string name)
|
||||||
|
{
|
||||||
|
int id = 0;
|
||||||
|
foreach (char c in name)
|
||||||
|
{
|
||||||
|
id = 31 * id + c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly HashSet<string> s_reserveNames = new HashSet<string>()
|
||||||
|
{
|
||||||
|
"end",
|
||||||
|
"base",
|
||||||
|
"super",
|
||||||
|
"const",
|
||||||
|
"is",
|
||||||
|
"as",
|
||||||
|
"typeid",
|
||||||
|
"typeof",
|
||||||
|
"object",
|
||||||
|
"ref",
|
||||||
|
"out",
|
||||||
|
"in",
|
||||||
|
"os",
|
||||||
|
"sb",
|
||||||
|
"ele",
|
||||||
|
"new",
|
||||||
|
"friend",
|
||||||
|
"public",
|
||||||
|
"protected",
|
||||||
|
"private",
|
||||||
|
"internal",
|
||||||
|
"return",
|
||||||
|
"static",
|
||||||
|
};
|
||||||
|
|
||||||
|
public static bool IsValidName(string name)
|
||||||
|
{
|
||||||
|
name = name.Trim();
|
||||||
|
return name.Length > 0 && !s_reserveNames.Contains(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string UpperCaseFirstChar(string s)
|
||||||
|
{
|
||||||
|
return char.ToUpper(s[0]) + s.Substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToCsStyleName(string orginName)
|
||||||
|
{
|
||||||
|
return string.Join("", orginName.Split('_').Select(c => UpperCaseFirstChar(c)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToJavaStyleName(string orginName)
|
||||||
|
{
|
||||||
|
var words = orginName.Split('_');
|
||||||
|
var s = new StringBuilder();
|
||||||
|
s.Append(words[0]);
|
||||||
|
for (int i = 1; i < words.Length; i++)
|
||||||
|
{
|
||||||
|
s.Append(UpperCaseFirstChar(words[i]));
|
||||||
|
}
|
||||||
|
return s.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToJavaGetterName(string orginName)
|
||||||
|
{
|
||||||
|
var words = orginName.Split('_');
|
||||||
|
var s = new StringBuilder("get");
|
||||||
|
foreach (var word in words)
|
||||||
|
{
|
||||||
|
s.Append(UpperCaseFirstChar(word));
|
||||||
|
}
|
||||||
|
return s.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetNamespace(string fullName)
|
||||||
|
{
|
||||||
|
var index = fullName.LastIndexOf('.');
|
||||||
|
return index >= 0 ? fullName.Substring(0, index) : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static string GetName(string fullName)
|
||||||
|
{
|
||||||
|
var index = fullName.LastIndexOf('.');
|
||||||
|
return index >= 0 ? fullName.Substring(index + 1, fullName.Length - index - 1) : fullName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="Source\TypeVisitors\Proto\**" />
|
||||||
|
<EmbeddedResource Remove="Source\TypeVisitors\Proto\**" />
|
||||||
|
<None Remove="Source\TypeVisitors\Proto\**" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="ExcelDataReader" Version="3.6.0" />
|
||||||
|
<PackageReference Include="NeoLua" Version="1.3.11" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Luban.Job.Common\Luban.Job.Common.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
using Luban.Job.Cfg.Datas;
|
||||||
|
using Luban.Job.Cfg.Defs;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Luban.Job.Cfg.Cache
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 配置加载记录缓存。
|
||||||
|
/// 如果某个表对应的数据文件未修改,定义没变化,那加载后的数据应该是一样的。
|
||||||
|
/// </summary>
|
||||||
|
class FileRecordCacheManager
|
||||||
|
{
|
||||||
|
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
public static FileRecordCacheManager Ins { get; } = new FileRecordCacheManager();
|
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<(string, string, string, bool), (DefTable, List<DType>)> _caches = new ConcurrentDictionary<(string, string, string, bool), (DefTable, List<DType>)>();
|
||||||
|
|
||||||
|
public bool TryGetCacheLoadedRecords(DefTable table, string md5, string originFile, string sheetName, bool exportTestData, out List<DType> cacheRecords)
|
||||||
|
{
|
||||||
|
// TODO text localization check
|
||||||
|
cacheRecords = null;
|
||||||
|
if (!_caches.TryGetValue((table.Assembly.TimeZone.Id, md5, sheetName, exportTestData), out var r))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (r.Item1.ValueTType.GetBeanAs<DefBean>().IsDefineEquals(table.ValueTType.GetBeanAs<DefBean>()))
|
||||||
|
{
|
||||||
|
cacheRecords = r.Item2;
|
||||||
|
s_logger.Trace("hit cache. table:{table} file:{file} md5:{md5}", table.FullName, originFile, md5);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddCacheLoadedRecords(DefTable table, string md5, string sheetName, bool exportTestData, List<DType> cacheRecords)
|
||||||
|
{
|
||||||
|
_caches[(table.Assembly.TimeZone.Id, md5, sheetName, exportTestData)] = (table, cacheRecords);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
using Luban.Job.Cfg.Datas;
|
||||||
|
using Luban.Job.Common.Types;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Luban.Job.Cfg.DataSources
|
||||||
|
{
|
||||||
|
abstract class AbstractDataSource
|
||||||
|
{
|
||||||
|
public abstract DType ReadOne(TBean type);
|
||||||
|
|
||||||
|
public abstract List<DType> ReadMulti(TBean type);
|
||||||
|
|
||||||
|
public abstract void Load(string rawUrl, string sheetName, Stream stream, bool exportDebugData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
using Luban.Job.Cfg.Datas;
|
||||||
|
using Luban.Job.Common.Types;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Luban.Job.Cfg.DataSources.Binary
|
||||||
|
{
|
||||||
|
class BinaryDataSource : AbstractDataSource
|
||||||
|
{
|
||||||
|
public override void Load(string rawUrl, string sheetName, Stream stream, bool exportDebugData)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override List<DType> ReadMulti(TBean type)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DType ReadOne(TBean type)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
using ExcelDataReader;
|
||||||
|
using Luban.Job.Cfg.Datas;
|
||||||
|
using Luban.Job.Common.Types;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
|
{
|
||||||
|
|
||||||
|
class ExcelDataSource : AbstractDataSource
|
||||||
|
{
|
||||||
|
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
private readonly List<Sheet> _sheets = new List<Sheet>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public override void Load(string rawUrl, string sheetName, Stream stream, bool exportTestData)
|
||||||
|
{
|
||||||
|
s_logger.Trace("{filename} {sheet}", rawUrl, sheetName);
|
||||||
|
string ext = Path.GetExtension(rawUrl);
|
||||||
|
using (var reader = ext != ".csv" ? ExcelReaderFactory.CreateReader(stream) : ExcelReaderFactory.CreateCsvReader(stream))
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (sheetName == null || reader.Name == sheetName)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sheet = ReadSheet(reader, exportTestData);
|
||||||
|
if (sheet != null)
|
||||||
|
{
|
||||||
|
_sheets.Add(sheet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new Exception($"excel:{rawUrl} sheet:{reader.Name} 读取失败. ==> {e.Message}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} while (reader.NextResult());
|
||||||
|
}
|
||||||
|
if (_sheets.Count == 0)
|
||||||
|
{
|
||||||
|
throw new Exception($"excel:{rawUrl} 不包含有效的单元薄(有效单元薄的A0单元格必须是##).");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Sheet ReadSheet(IExcelDataReader reader, bool exportTestData)
|
||||||
|
{
|
||||||
|
var sheet = new Sheet(reader.Name ?? "", exportTestData);
|
||||||
|
return sheet.Load(reader) ? sheet : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override List<DType> ReadMulti(TBean type)
|
||||||
|
{
|
||||||
|
var datas = new List<DType>();
|
||||||
|
foreach (var sheet in _sheets)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
datas.AddRange(sheet.ReadMulti(type));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new Exception($"sheet:{sheet.Name} ==> {e.Message} {e.StackTrace}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return datas;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DType ReadOne(TBean type)
|
||||||
|
{
|
||||||
|
var datas = ReadMulti(type);
|
||||||
|
switch (datas.Count)
|
||||||
|
{
|
||||||
|
case 1: return datas[0];
|
||||||
|
case 0: throw new Exception($"单例表不能为空,必须包含且只包含1个记录");
|
||||||
|
default: throw new Exception($"单例表必须恰好包含1个记录. 但当前记录数为:{datas.Count}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,182 @@
|
||||||
|
using Luban.Job.Cfg.Utils;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
|
{
|
||||||
|
class ExcelStream
|
||||||
|
{
|
||||||
|
|
||||||
|
private readonly List<Sheet.Cell> _datas;
|
||||||
|
private readonly int _toIndex;
|
||||||
|
private int _curIndex;
|
||||||
|
|
||||||
|
public ExcelStream(List<Sheet.Cell> datas, int fromIndex, int toIndex, string sep)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(sep))
|
||||||
|
{
|
||||||
|
this._datas = datas;
|
||||||
|
this._toIndex = toIndex;
|
||||||
|
this._curIndex = fromIndex;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this._datas = new List<Sheet.Cell>();
|
||||||
|
for (int i = fromIndex; i <= toIndex; i++)
|
||||||
|
{
|
||||||
|
var cell = datas[i];
|
||||||
|
object d = cell.Value;
|
||||||
|
if (d is string s)
|
||||||
|
{
|
||||||
|
this._datas.AddRange(DataUtil.SplitStringByAnySepChar(s, sep).Select(x => new Sheet.Cell(cell.Row, cell.Column, x)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this._datas.Add(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._curIndex = 0;
|
||||||
|
this._toIndex = this._datas.Count - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExcelStream(Sheet.Cell cell, string sep)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(sep))
|
||||||
|
{
|
||||||
|
this._datas = new List<Sheet.Cell> { cell };
|
||||||
|
this._toIndex = 0;
|
||||||
|
this._curIndex = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this._datas = new List<Sheet.Cell>();
|
||||||
|
object d = cell.Value;
|
||||||
|
if (!IsSkip(d))
|
||||||
|
{
|
||||||
|
if (d is string s)
|
||||||
|
{
|
||||||
|
this._datas.AddRange(DataUtil.SplitStringByAnySepChar(s, sep).Where(x => !IsSkip(x)).Select(x => new Sheet.Cell(cell.Row, cell.Column, x)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this._datas.Add(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._curIndex = 0;
|
||||||
|
this._toIndex = this._datas.Count - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string First => _datas[_curIndex].Value?.ToString();
|
||||||
|
|
||||||
|
public string CurrentExcelPosition => _datas[Math.Min(_curIndex, _datas.Count - 1)].ToString();
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.Append('[');
|
||||||
|
for (int i = _curIndex; i <= _toIndex; i++)
|
||||||
|
{
|
||||||
|
sb.Append(_datas[i].Value);
|
||||||
|
sb.Append(',');
|
||||||
|
}
|
||||||
|
sb.Append(']');
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryRead(out object data)
|
||||||
|
{
|
||||||
|
data = null;
|
||||||
|
while (_curIndex <= _toIndex)
|
||||||
|
{
|
||||||
|
data = _datas[_curIndex++].Value;
|
||||||
|
if (!IsSkip(data))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Read()
|
||||||
|
{
|
||||||
|
//if (curIndex <= toIndex)
|
||||||
|
//{
|
||||||
|
// return datas[curIndex++].Value;
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// throw new Exception($"cell:{datas[curIndex - 1]} 无法读取到足够多的数据");
|
||||||
|
//}
|
||||||
|
return ReadSkipNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
//public object Read(bool nullable)
|
||||||
|
//{
|
||||||
|
// return nullable ? Read() : ReadSkipNull();
|
||||||
|
//}
|
||||||
|
|
||||||
|
public Sheet.Cell ReadCell()
|
||||||
|
{
|
||||||
|
while (_curIndex <= _toIndex)
|
||||||
|
{
|
||||||
|
var data = _datas[_curIndex++];
|
||||||
|
if (!IsSkip(data.Value))
|
||||||
|
{
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Exception($"cell:{_datas[_curIndex - 1]} 缺少数据");
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ReadSkipNull()
|
||||||
|
{
|
||||||
|
while (_curIndex <= _toIndex)
|
||||||
|
{
|
||||||
|
var data = _datas[_curIndex++];
|
||||||
|
if (!IsSkip(data.Value))
|
||||||
|
{
|
||||||
|
return data.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Exception($"cell:{_datas[_curIndex - 1]} 缺少数据");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private const string END_OF_LIST = "}";
|
||||||
|
|
||||||
|
|
||||||
|
private bool IsSkip(object x)
|
||||||
|
{
|
||||||
|
return x == null || (x is string s && string.IsNullOrEmpty(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryReadEOF()
|
||||||
|
{
|
||||||
|
int oldIndex = _curIndex;
|
||||||
|
while (_curIndex <= _toIndex)
|
||||||
|
{
|
||||||
|
var value = _datas[_curIndex++].Value?.ToString();
|
||||||
|
if (!IsSkip(value))
|
||||||
|
{
|
||||||
|
if (value == END_OF_LIST)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_curIndex = oldIndex;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
_curIndex = oldIndex;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,660 @@
|
||||||
|
using ExcelDataReader;
|
||||||
|
using Luban.Job.Cfg.Datas;
|
||||||
|
using Luban.Job.Cfg.TypeVisitors;
|
||||||
|
using Luban.Job.Common.Types;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
|
{
|
||||||
|
class Sheet
|
||||||
|
{
|
||||||
|
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
private bool OrientRow { get; set; } = true; // 以行为数据读取方向
|
||||||
|
|
||||||
|
private bool Align { get; set; } = true;// 标题头与数据严格对齐的 固定列格式
|
||||||
|
|
||||||
|
private bool IsMultiRow { get; set; }
|
||||||
|
|
||||||
|
private int TitleRows { get; set; } = 3; // 默认有三行是标题行. 第一行是字段名,第二行是中文描述,第三行是注释
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
private List<List<Cell>> _rowColumns;
|
||||||
|
|
||||||
|
private Title _rootTitle;
|
||||||
|
|
||||||
|
private bool ExportTestData { get; }
|
||||||
|
|
||||||
|
public class Title
|
||||||
|
{
|
||||||
|
public int FromIndex { get; set; }
|
||||||
|
|
||||||
|
public int ToIndex { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public Dictionary<string, Title> SubTitles { get; set; } = new Dictionary<string, Title>();
|
||||||
|
|
||||||
|
public List<Title> SubTitleList { get; set; } = new List<Title>();
|
||||||
|
|
||||||
|
public void AddSubTitle(Title title)
|
||||||
|
{
|
||||||
|
if (!SubTitles.TryAdd(title.Name, title))
|
||||||
|
{
|
||||||
|
throw new Exception($"标题:{title.Name} 重复");
|
||||||
|
}
|
||||||
|
SubTitleList.Add(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 由于先处理merge再处理只占一列的标题头.
|
||||||
|
// sub titles 未必是有序的。对于大多数数据并无影响
|
||||||
|
// 但对于 list类型的多级标题头,有可能导致element 数据次序乱了
|
||||||
|
public void SortSubTitles()
|
||||||
|
{
|
||||||
|
SubTitleList.Sort((t1, t2) => t1.FromIndex - t2.FromIndex);
|
||||||
|
foreach (var t in SubTitleList)
|
||||||
|
{
|
||||||
|
t.SortSubTitles();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"name:{Name} [{FromIndex}, {ToIndex}] sub titles:[{string.Join(",\\n", SubTitleList)}]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Cell
|
||||||
|
{
|
||||||
|
public Cell(int row, int column, object value)
|
||||||
|
{
|
||||||
|
this.Row = row;
|
||||||
|
this.Column = column;
|
||||||
|
this.Value = value;
|
||||||
|
}
|
||||||
|
public int Row { get; } // 从 1 开始
|
||||||
|
|
||||||
|
public int Column { get; } // 从 0 开始,考虑改了它?
|
||||||
|
|
||||||
|
public object Value { get; }
|
||||||
|
|
||||||
|
|
||||||
|
private static string ToAlphaString(int column)
|
||||||
|
{
|
||||||
|
int h = column / 26;
|
||||||
|
int n = column % 26;
|
||||||
|
return $"{(h > 0 ? ((char)('A' + h - 1)).ToString() : "")}{(char)('A' + n)}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"[{ToAlphaString(Column)}:{Row + 1}] {Value}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NamedRow
|
||||||
|
{
|
||||||
|
public Title SelfTitle { get; }
|
||||||
|
|
||||||
|
public List<List<Cell>> Rows { get; }
|
||||||
|
|
||||||
|
public Dictionary<string, Title> Titles => SelfTitle.SubTitles;
|
||||||
|
|
||||||
|
public List<Title> TitleList => SelfTitle.SubTitleList;
|
||||||
|
|
||||||
|
public NamedRow(Title selfTitle, List<Cell> row)
|
||||||
|
{
|
||||||
|
SelfTitle = selfTitle;
|
||||||
|
Rows = new List<List<Cell>>() { row };
|
||||||
|
}
|
||||||
|
|
||||||
|
public NamedRow(Title selfTitle, List<List<Cell>> rows)
|
||||||
|
{
|
||||||
|
SelfTitle = selfTitle;
|
||||||
|
Rows = rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int RowCount => Rows.Count;
|
||||||
|
|
||||||
|
private void CheckEmptySinceSecondRow(string name, int fromIndex, int toIndex)
|
||||||
|
{
|
||||||
|
for (int i = 1; i < Rows.Count; i++)
|
||||||
|
{
|
||||||
|
var row = Rows[i];
|
||||||
|
if (!IsBlankRow(row, fromIndex, toIndex))
|
||||||
|
{
|
||||||
|
throw new Exception($"字段:{name} 不是多行字段,只能第一行填值. {Bright.Common.StringUtil.CollectionToString(row)}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Title GetTitle(string name)
|
||||||
|
{
|
||||||
|
return Titles.TryGetValue(name, out var title) ? title : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExcelStream GetColumn(string name, string sep)
|
||||||
|
{
|
||||||
|
if (Titles.TryGetValue(name, out var title))
|
||||||
|
{
|
||||||
|
CheckEmptySinceSecondRow(name, title.FromIndex, title.ToIndex);
|
||||||
|
var es = new ExcelStream(Rows[0], title.FromIndex, title.ToIndex, sep);
|
||||||
|
return es;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception($"单元薄 缺失 列:{name},请检查是否写错或者遗漏");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public NamedRow GetSubTitleNamedRow(string name)
|
||||||
|
{
|
||||||
|
Title title = this.Titles[name];
|
||||||
|
CheckEmptySinceSecondRow(name, title.FromIndex, title.ToIndex);
|
||||||
|
return new NamedRow(title, this.Rows[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NamedRow GetSubTitleNamedRowOfMultiRows(string name)
|
||||||
|
{
|
||||||
|
Title title = Titles[name];
|
||||||
|
return new NamedRow(title, this.Rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<NamedRow> GenerateSubNameRows()
|
||||||
|
{
|
||||||
|
foreach (var row in Rows)
|
||||||
|
{
|
||||||
|
if (SelfTitle != null ? IsBlankRow(row, SelfTitle.FromIndex, SelfTitle.ToIndex) : IsBlankRow(row))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
yield return new NamedRow(SelfTitle, row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<ExcelStream> GetColumnOfMultiRows(string name, string sep)
|
||||||
|
{
|
||||||
|
if (Titles.TryGetValue(name, out var title))
|
||||||
|
{
|
||||||
|
foreach (var row in Rows)
|
||||||
|
{
|
||||||
|
if (IsBlankRow(row, title.FromIndex, title.ToIndex))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
yield return new ExcelStream(row, title.FromIndex, title.ToIndex, sep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception($"单元薄 缺失 列:{name},请检查是否写错或者遗漏");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Sheet(string name, bool exportTestData)
|
||||||
|
{
|
||||||
|
this.Name = name;
|
||||||
|
this.ExportTestData = exportTestData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Load(IExcelDataReader reader)
|
||||||
|
{
|
||||||
|
//s_logger.Info("read sheet:{sheet}", reader.Name);
|
||||||
|
if (!ParseMeta(reader))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_logger.Trace("align:{align} row:{orient}", Align, OrientRow);
|
||||||
|
if (!Align)
|
||||||
|
{
|
||||||
|
throw new Exception($"当前不支持 align:false");
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadRemainRows(reader);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ParseMeta(IExcelDataReader reader)
|
||||||
|
{
|
||||||
|
if (!reader.Read() || reader.FieldCount == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// meta 行 必须以 ##为第一个单元格内容,紧接着 key:value 形式 表达meta属性
|
||||||
|
if (reader.GetString(0) != "##")
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1, n = reader.FieldCount; i < n; i++)
|
||||||
|
{
|
||||||
|
var attr = reader.GetString(i);
|
||||||
|
if (string.IsNullOrWhiteSpace(attr))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ss = attr.Split(':');
|
||||||
|
if (ss.Length != 2)
|
||||||
|
{
|
||||||
|
throw new Exception($"单元薄 meta 定义出错. attribute:{attr}");
|
||||||
|
}
|
||||||
|
string key = ss[0].ToLower();
|
||||||
|
string value = ss[1];
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
case "align":
|
||||||
|
{
|
||||||
|
if (!bool.TryParse(value, out var v))
|
||||||
|
{
|
||||||
|
throw new Exception($"单元薄 meta 定义 align:{value} 属性值只能为true或false");
|
||||||
|
}
|
||||||
|
Align = v;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "row":
|
||||||
|
{
|
||||||
|
if (!bool.TryParse(value, out var v))
|
||||||
|
{
|
||||||
|
throw new Exception($"单元薄 meta 定义 row:{value} 属性值只能为true或false");
|
||||||
|
}
|
||||||
|
OrientRow = v;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "multi_rows":
|
||||||
|
{
|
||||||
|
if (!bool.TryParse(value, out var v))
|
||||||
|
{
|
||||||
|
throw new Exception($"单元薄 meta 定义 multi_rows:{value} 属性值只能为true或false");
|
||||||
|
}
|
||||||
|
IsMultiRow = v;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "title_rows":
|
||||||
|
{
|
||||||
|
if (!int.TryParse(value, out var v))
|
||||||
|
{
|
||||||
|
throw new Exception($"单元薄 meta 定义 title_rows:{value} 属性值只能为整数[1,10]");
|
||||||
|
}
|
||||||
|
if (v < 1 || v > 10)
|
||||||
|
{
|
||||||
|
throw new Exception($"单元薄 title_rows 应该在 [1,10] 范围内,默认是3");
|
||||||
|
}
|
||||||
|
TitleRows = v;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "ignore":
|
||||||
|
{
|
||||||
|
if (!bool.TryParse(value, out var v))
|
||||||
|
{
|
||||||
|
throw new Exception($"单元薄 meta 定义 ignore:{value} 属性值只能为true或false");
|
||||||
|
}
|
||||||
|
if (v)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
throw new Exception($"非法单元薄 meta 属性定义 {attr}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool NotExport(List<Cell> row)
|
||||||
|
{
|
||||||
|
if (row.Count == 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (row[0].Value == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string exportFlag = row[0].Value.ToString().Trim().ToLower();
|
||||||
|
switch (exportFlag)
|
||||||
|
{
|
||||||
|
case "false":
|
||||||
|
case "否": return true;
|
||||||
|
case "true":
|
||||||
|
case "是": return false;
|
||||||
|
case "test":
|
||||||
|
case "测试":
|
||||||
|
{
|
||||||
|
if (!ExportTestData)
|
||||||
|
{
|
||||||
|
s_logger.Debug("忽略测试数据. row:{row}", row);
|
||||||
|
}
|
||||||
|
return !ExportTestData;
|
||||||
|
}
|
||||||
|
default: throw new Exception($"不支持的excel 导出标记: {exportFlag}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitSubTitles(Title parentTitle, List<List<Cell>> rows, CellRange[] mergeCells, int maxDepth, int depth, int fromColumn, int toColumn)
|
||||||
|
{
|
||||||
|
List<Cell> row = rows[depth];
|
||||||
|
|
||||||
|
//if (row.Count > fromColumn)
|
||||||
|
//{
|
||||||
|
// row = row.GetRange(fromColumn, Math.Min(row.Count, toColumn + 1) - fromColumn);
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
foreach (var mergeCell in mergeCells)
|
||||||
|
{
|
||||||
|
if (mergeCell.FromRow == depth + 1 && mergeCell.FromColumn >= fromColumn && mergeCell.ToColumn <= toColumn)
|
||||||
|
{
|
||||||
|
string subTitleName = row[mergeCell.FromColumn].Value?.ToString().Trim();
|
||||||
|
if (!string.IsNullOrWhiteSpace(subTitleName))
|
||||||
|
{
|
||||||
|
var newTitle = new Title() { Name = subTitleName, FromIndex = mergeCell.FromColumn, ToIndex = mergeCell.ToColumn };
|
||||||
|
if (depth + 1 < maxDepth)
|
||||||
|
{
|
||||||
|
InitSubTitles(newTitle, rows, mergeCells, maxDepth, depth + 1, mergeCell.FromColumn, mergeCell.ToColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
parentTitle.AddSubTitle(newTitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = fromColumn; i <= toColumn; i++)
|
||||||
|
{
|
||||||
|
if (i >= row.Count)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var name = row[i].Value?.ToString()?.Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(name))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentTitle.SubTitles.TryGetValue(name, out var oldTitle))
|
||||||
|
{
|
||||||
|
if (oldTitle.FromIndex != i)
|
||||||
|
{
|
||||||
|
throw new Exception($"sub title 列:{name} 重复");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var newTitle = new Title() { Name = name, FromIndex = i, ToIndex = i };
|
||||||
|
if (depth + 1 < maxDepth)
|
||||||
|
{
|
||||||
|
InitSubTitles(newTitle, rows, mergeCells, maxDepth, depth + 1, i, i);
|
||||||
|
}
|
||||||
|
parentTitle.AddSubTitle(newTitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void LoadRemainRows(IExcelDataReader reader)
|
||||||
|
{
|
||||||
|
// TODO 优化性能
|
||||||
|
// 几个思路
|
||||||
|
// 1. 没有 title 的列不加载
|
||||||
|
// 2. 空行优先跳过
|
||||||
|
// 3. 跳过null或者empty的单元格
|
||||||
|
var rows = new List<List<Cell>>();
|
||||||
|
int rowIndex = 0;
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
++rowIndex; // 第一行是 meta ,跳过
|
||||||
|
var row = new List<Cell>();
|
||||||
|
for (int i = 0, n = reader.FieldCount; i < n; i++)
|
||||||
|
{
|
||||||
|
row.Add(new Cell(rowIndex, i, reader.GetValue(i)));
|
||||||
|
}
|
||||||
|
rows.Add(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OrientRow)
|
||||||
|
{
|
||||||
|
this._rowColumns = rows;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 转置这个行列
|
||||||
|
int maxColumn = rows.Select(r => r.Count).Max();
|
||||||
|
this._rowColumns = new List<List<Cell>>();
|
||||||
|
for (int i = 0; i < maxColumn; i++)
|
||||||
|
{
|
||||||
|
var row = new List<Cell>();
|
||||||
|
for (int j = 0; j < rows.Count; j++)
|
||||||
|
{
|
||||||
|
row.Add(i < rows[i].Count ? rows[j][i] : new Cell(j + 1, i, null));
|
||||||
|
}
|
||||||
|
this._rowColumns.Add(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._rowColumns.Count < 1)
|
||||||
|
{
|
||||||
|
throw new Exception($"没有定义字段名行");
|
||||||
|
}
|
||||||
|
|
||||||
|
_rootTitle = new Title() { Name = "_root_", FromIndex = 1, ToIndex = rows.Select(r => r.Count).Max() - 1 };
|
||||||
|
|
||||||
|
int titleRowNum = 1;
|
||||||
|
if (reader.MergeCells != null)
|
||||||
|
{
|
||||||
|
if (OrientRow)
|
||||||
|
{
|
||||||
|
foreach (var mergeCell in reader.MergeCells)
|
||||||
|
{
|
||||||
|
if (mergeCell.FromRow == 1 && mergeCell.FromColumn == 0 && mergeCell.ToColumn == 0)
|
||||||
|
{
|
||||||
|
titleRowNum = mergeCell.ToRow - mergeCell.FromRow + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
foreach (var mergeCell in reader.MergeCells)
|
||||||
|
{
|
||||||
|
if (OrientRow)
|
||||||
|
{
|
||||||
|
//if (mergeCell.FromRow <= 1 && mergeCell.ToRow >= 1)
|
||||||
|
if (mergeCell.FromRow == 1)
|
||||||
|
{
|
||||||
|
// 标题 行
|
||||||
|
titleRowNum = Math.Max(titleRowNum, mergeCell.ToRow - mergeCell.FromRow + 1);
|
||||||
|
var titleName = _rowColumns[0][mergeCell.FromColumn].Value?.ToString()?.Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(titleName))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newTitle = new Title() { Name = titleName, FromIndex = mergeCell.FromColumn, ToIndex = mergeCell.ToColumn };
|
||||||
|
if (titleRowNum > 1)
|
||||||
|
{
|
||||||
|
InitSubTitles(newTitle, rows, reader.MergeCells, titleRowNum, 1, mergeCell.FromColumn, mergeCell.ToColumn);
|
||||||
|
}
|
||||||
|
_rootTitle.AddSubTitle(newTitle);
|
||||||
|
//s_logger.Info("=== sheet:{sheet} title:{title}", Name, newTitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (mergeCell.FromColumn <= 0 && mergeCell.ToColumn >= 0)
|
||||||
|
{
|
||||||
|
// 标题 行
|
||||||
|
var titleName = _rowColumns[0][mergeCell.FromRow - 1].Value?.ToString()?.Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(titleName))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_rootTitle.AddSubTitle(new Title() { Name = titleName, FromIndex = mergeCell.FromRow - 1, ToIndex = mergeCell.ToRow - 1 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO 其实有bug. 未处理只占一列的 多级标题头
|
||||||
|
|
||||||
|
// 有一些列不是MergeCell,所以还需要额外处理
|
||||||
|
var titleRow = _rowColumns[0];
|
||||||
|
for (int i = 0; i < titleRow.Count; i++)
|
||||||
|
{
|
||||||
|
var name = titleRow[i].Value?.ToString()?.Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(name))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_rootTitle.SubTitles.TryGetValue(name, out var oldTitle))
|
||||||
|
{
|
||||||
|
if (oldTitle.FromIndex != i)
|
||||||
|
{
|
||||||
|
throw new Exception($"列:{name} 重复");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_rootTitle.AddSubTitle(new Title() { Name = name, FromIndex = i, ToIndex = i });
|
||||||
|
}
|
||||||
|
if (_rootTitle.SubTitleList.Count == 0)
|
||||||
|
{
|
||||||
|
throw new Exception($"没有定义任何有效 列");
|
||||||
|
}
|
||||||
|
_rootTitle.SortSubTitles();
|
||||||
|
foreach (var title in _rootTitle.SubTitleList)
|
||||||
|
{
|
||||||
|
// s_logger.Info("============ sheet:{sheet} title:{title}", Name, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除标题行
|
||||||
|
this._rowColumns.RemoveRange(0, Math.Min(TitleRows + titleRowNum - 1, this._rowColumns.Count));
|
||||||
|
|
||||||
|
this._rowColumns.RemoveAll(row => NotExport(row));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static bool IsBlankRow(List<Cell> row)
|
||||||
|
{
|
||||||
|
// 第一列被策划用于表示是否注释掉此行
|
||||||
|
// 忽略此列是否空白
|
||||||
|
return row.GetRange(1, row.Count - 1).All(c => c.Value == null || (c.Value is string s && string.IsNullOrWhiteSpace(s)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsBlankRow(List<Cell> row, int fromIndex, int toIndex)
|
||||||
|
{
|
||||||
|
for (int i = Math.Max(1, fromIndex), n = Math.Min(toIndex, row.Count - 1); i <= n; i++)
|
||||||
|
{
|
||||||
|
var v = row[i].Value;
|
||||||
|
if (v != null && !(v is string s && string.IsNullOrEmpty(s)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Cell> GetNextRecordRow()
|
||||||
|
{
|
||||||
|
while (curReadIndex < _rowColumns.Count)
|
||||||
|
{
|
||||||
|
var row = _rowColumns[curReadIndex++];
|
||||||
|
if (IsBlankRow(row))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasNotMainKey(List<Cell> row)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(row[1].Value?.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<List<Cell>> GetNextRecordRows()
|
||||||
|
{
|
||||||
|
List<List<Cell>> rows = null;
|
||||||
|
while (curReadIndex < _rowColumns.Count)
|
||||||
|
{
|
||||||
|
var row = _rowColumns[curReadIndex++];
|
||||||
|
if (IsBlankRow(row))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rows == null)
|
||||||
|
{
|
||||||
|
rows = new List<List<Cell>>() { row };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (HasNotMainKey(row))
|
||||||
|
{
|
||||||
|
rows.Add(row);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
--curReadIndex;
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public List<DType> ReadMulti(TBean type)
|
||||||
|
{
|
||||||
|
var datas = new List<DType>();
|
||||||
|
|
||||||
|
for (DType data; (data = ReadOne(type)) != null;)
|
||||||
|
{
|
||||||
|
datas.Add(data);
|
||||||
|
}
|
||||||
|
return datas;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int curReadIndex = 0;
|
||||||
|
public DType ReadOne(TBean type)
|
||||||
|
{
|
||||||
|
if (!IsMultiRow)
|
||||||
|
{
|
||||||
|
List<Cell> row = GetNextRecordRow();
|
||||||
|
if (row == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ExcelNamedRowDataCreator.Ins.ReadExcel(new NamedRow(_rootTitle, row), type);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
List<List<Cell>> rows = GetNextRecordRows();
|
||||||
|
if (rows == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ExcelNamedRowDataCreator.Ins.ReadExcel(new NamedRow(_rootTitle, rows), type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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()} 不一致");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,309 @@
|
||||||
|
using Luban.Job.Cfg.Defs;
|
||||||
|
using Scriban;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Luban.Job.Cfg.Generate
|
||||||
|
{
|
||||||
|
class CsJsonCodeRender : CsCodeRenderBase
|
||||||
|
{
|
||||||
|
[ThreadStatic]
|
||||||
|
private static Template t_beanRender;
|
||||||
|
public override string Render(DefBean b)
|
||||||
|
{
|
||||||
|
var template = t_beanRender ??= Template.Parse(@"
|
||||||
|
using Bright.Serialization;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
{{
|
||||||
|
name = x.name
|
||||||
|
parent_def_type = x.parent_def_type
|
||||||
|
parent = x.parent
|
||||||
|
export_fields = x.export_fields
|
||||||
|
hierarchy_export_fields = x.hierarchy_export_fields
|
||||||
|
}}
|
||||||
|
|
||||||
|
namespace {{x.namespace_with_top_module}}
|
||||||
|
{
|
||||||
|
|
||||||
|
public {{x.cs_class_modifier}} partial class {{name}} : {{if parent_def_type}} {{parent}} {{else}} Bright.Config.BeanBase {{end}}
|
||||||
|
{
|
||||||
|
public {{name}}(JsonElement _buf) {{if parent_def_type}} : base(_buf) {{end}}
|
||||||
|
{
|
||||||
|
{{~ for field in export_fields ~}}
|
||||||
|
{{cs_json_deserialize '_buf' field.cs_style_name field.name field.ctype}}
|
||||||
|
{{~if field.index_field~}}
|
||||||
|
foreach(var _v in {{field.cs_style_name}}) { {{field.cs_style_name}}_Index.Add(_v.{{field.index_field.cs_style_name}}, _v); }
|
||||||
|
{{~end~}}
|
||||||
|
{{~end~}}
|
||||||
|
}
|
||||||
|
|
||||||
|
public {{name}}({{- for field in hierarchy_export_fields }}{{cs_define_type field.ctype}} {{field.name}}{{if !for.last}},{{end}} {{end}}) {{if parent_def_type}} : base({{- for field in parent_def_type.hierarchy_export_fields }}{{field.name}}{{if !for.last}},{{end}}{{end}}) {{end}}
|
||||||
|
{
|
||||||
|
{{- for field in export_fields }}
|
||||||
|
this.{{field.cs_style_name}} = {{field.name}};
|
||||||
|
{{-if field.index_field}}
|
||||||
|
foreach(var _v in {{field.cs_style_name}}) { {{field.cs_style_name}}_Index.Add(_v.{{field.index_field.cs_style_name}}, _v); }
|
||||||
|
{{-end}}
|
||||||
|
{{-end}}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static {{name}} Deserialize{{name}}(JsonElement _buf)
|
||||||
|
{
|
||||||
|
{{if x.is_abstract_type}}
|
||||||
|
if (_buf.ValueKind == JsonValueKind.Null) { return null; }
|
||||||
|
switch (_buf.GetProperty(""__type__"").GetString())
|
||||||
|
{
|
||||||
|
{{- for child in x.hierarchy_not_abstract_children}}
|
||||||
|
case ""{{child.name}}"": return new {{child.full_name}}(_buf);
|
||||||
|
{{-end}}
|
||||||
|
default: throw new SerializationException();
|
||||||
|
}
|
||||||
|
{{else}}
|
||||||
|
return new {{x.full_name}}(_buf);
|
||||||
|
{{end}}
|
||||||
|
}
|
||||||
|
|
||||||
|
{{~ for field in export_fields ~}}
|
||||||
|
public readonly {{cs_define_type field.ctype}} {{field.cs_style_name}};
|
||||||
|
{{~if field.index_field~}}
|
||||||
|
public readonly Dictionary<{{cs_define_type field.index_field.ctype}}, {{cs_define_type field.ctype.element_type}}> {{field.cs_style_name}}_Index = new Dictionary<{{cs_define_type field.index_field.ctype}}, {{cs_define_type field.ctype.element_type}}>();
|
||||||
|
{{~end~}}
|
||||||
|
{{~if field.gen_ref~}}
|
||||||
|
public {{field.cs_ref_validator_define}}
|
||||||
|
{{~end~}}
|
||||||
|
{{~end~}}
|
||||||
|
|
||||||
|
{{if !x.is_abstract_type}}
|
||||||
|
public const int ID = {{x.id}};
|
||||||
|
public override int GetTypeId() => ID;
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
public {{x.cs_method_modifier}} void Resolve(Dictionary<string, object> _tables)
|
||||||
|
{
|
||||||
|
{{~if parent_def_type}}base.Resolve(_tables);{{end}}
|
||||||
|
{{~ for field in export_fields ~}}
|
||||||
|
{{~if field.gen_ref~}}
|
||||||
|
{{cs_ref_validator_resolve field}}
|
||||||
|
{{~else if field.has_recursive_ref~}}
|
||||||
|
{{cs_recursive_resolve field '_tables'}}
|
||||||
|
{{~end~}}
|
||||||
|
{{~end~}}
|
||||||
|
OnResolveFinish(_tables);
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnResolveFinish(Dictionary<string, object> _tables);
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return ""{{full_name}}{ ""
|
||||||
|
{{- for field in hierarchy_export_fields }}
|
||||||
|
+ ""{{field.cs_style_name}}:"" + {{cs_to_string field.cs_style_name field.ctype}} + "",""
|
||||||
|
{{-end}}
|
||||||
|
+ ""}"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
");
|
||||||
|
var result = template.RenderCode(b);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static Template t_tableRender;
|
||||||
|
public override string Render(DefTable p)
|
||||||
|
{
|
||||||
|
var template = t_tableRender ??= Template.Parse(@"
|
||||||
|
using Bright.Serialization;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
{{
|
||||||
|
name = x.name
|
||||||
|
key_type = x.key_ttype
|
||||||
|
key_type1 = x.key_ttype1
|
||||||
|
key_type2 = x.key_ttype2
|
||||||
|
value_type = x.value_ttype
|
||||||
|
}}
|
||||||
|
|
||||||
|
namespace {{x.namespace_with_top_module}}
|
||||||
|
{
|
||||||
|
public sealed partial class {{name}}
|
||||||
|
{
|
||||||
|
{{~ if x.is_two_key_map_table ~}}
|
||||||
|
private readonly Dictionary<{{cs_define_type key_type1}}, List<{{cs_define_type value_type}}>> _dataListMap;
|
||||||
|
private readonly Dictionary<{{cs_define_type key_type1}}, Dictionary<{{cs_define_type key_type2}}, {{cs_define_type value_type}}>> _dataMapMap;
|
||||||
|
private readonly List<{{cs_define_type value_type}}> _dataList;
|
||||||
|
public {{name}}(JsonElement _buf)
|
||||||
|
{
|
||||||
|
_dataListMap = new Dictionary<{{cs_define_type key_type1}}, List<{{cs_define_type value_type}}>>();
|
||||||
|
_dataMapMap = new Dictionary<{{cs_define_type key_type1}}, Dictionary<{{cs_define_type key_type2}}, {{cs_define_type value_type}}>>();
|
||||||
|
_dataList = new List<{{cs_define_type value_type}}>();
|
||||||
|
|
||||||
|
foreach(JsonElement _row in _buf.EnumerateArray())
|
||||||
|
{
|
||||||
|
var _v = {{cs_define_type value_type}}.Deserialize{{value_type.bean.name}}(_row);
|
||||||
|
_dataList.Add(_v);
|
||||||
|
var _key = _v.{{x.index_field1.cs_style_name}};
|
||||||
|
if (!_dataListMap.TryGetValue(_key, out var list))
|
||||||
|
{
|
||||||
|
list = new List<{{cs_define_type value_type}}>();
|
||||||
|
_dataListMap.Add(_key, list);
|
||||||
|
}
|
||||||
|
list.Add(_v);
|
||||||
|
if (!_dataMapMap.TryGetValue(_key, out var map))
|
||||||
|
{
|
||||||
|
map = new Dictionary<{{cs_define_type key_type2}}, {{cs_define_type value_type}}>();
|
||||||
|
_dataMapMap.Add(_key, map);
|
||||||
|
}
|
||||||
|
map.Add(_v.{{x.index_field2.cs_style_name}}, _v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<{{cs_define_type key_type1}}, List<{{cs_define_type value_type}}>> DataListMap => _dataListMap;
|
||||||
|
public Dictionary<{{cs_define_type key_type1}}, Dictionary<{{cs_define_type key_type2}}, {{cs_define_type value_type}}>> DataMapMap => _dataMapMap;
|
||||||
|
public List<{{cs_define_type value_type}}> DataList => _dataList;
|
||||||
|
|
||||||
|
{{if value_type.is_dynamic}}
|
||||||
|
public T GetOrDefaultAs<T>({{cs_define_type key_type1}} key1, {{cs_define_type key_type2}} key2) where T : {{cs_define_type value_type}} => _dataMapMap.TryGetValue(key1, out var m) && m.TryGetValue(key2, out var v) ? (T)v : null;
|
||||||
|
public T GetAs<T>({{cs_define_type key_type1}} key1, {{cs_define_type key_type2}} key2) where T : {{cs_define_type value_type}} => (T)_dataMapMap[key1][key2];
|
||||||
|
{{end}}
|
||||||
|
public {{cs_define_type value_type}} GetOrDefault({{cs_define_type key_type1}} key1, {{cs_define_type key_type2}} key2) => _dataMapMap.TryGetValue(key1, out var m) && m.TryGetValue(key2, out var v) ? v : null;
|
||||||
|
public {{cs_define_type value_type}} Get({{cs_define_type key_type1}} key1, {{cs_define_type key_type2}} key2) => _dataMapMap[key1][key2];
|
||||||
|
public {{cs_define_type value_type}} this[{{cs_define_type key_type1}} key1, {{cs_define_type key_type2}} key2] => _dataMapMap[key1][key2];
|
||||||
|
|
||||||
|
public void Resolve(Dictionary<string, object> _tables)
|
||||||
|
{
|
||||||
|
foreach(var v in _dataList)
|
||||||
|
{
|
||||||
|
v.Resolve(_tables);
|
||||||
|
}
|
||||||
|
OnResolveFinish(_tables);
|
||||||
|
}
|
||||||
|
{{~else if x.is_map_table ~}}
|
||||||
|
private readonly Dictionary<{{cs_define_type key_type}}, {{cs_define_type value_type}}> _dataMap;
|
||||||
|
private readonly List<{{cs_define_type value_type}}> _dataList;
|
||||||
|
|
||||||
|
public {{name}}(JsonElement _buf)
|
||||||
|
{
|
||||||
|
_dataMap = new Dictionary<{{cs_define_type key_type}}, {{cs_define_type value_type}}>();
|
||||||
|
_dataList = new List<{{cs_define_type value_type}}>();
|
||||||
|
|
||||||
|
foreach(JsonElement _row in _buf.EnumerateArray())
|
||||||
|
{
|
||||||
|
var _v = {{cs_define_type value_type}}.Deserialize{{value_type.bean.name}}(_row);
|
||||||
|
_dataList.Add(_v);
|
||||||
|
_dataMap.Add(_v.{{x.index_field.cs_style_name}}, _v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<{{cs_define_type key_type}}, {{cs_define_type value_type}}> DataMap => _dataMap;
|
||||||
|
public List<{{cs_define_type value_type}}> DataList => _dataList;
|
||||||
|
|
||||||
|
{{~if value_type.is_dynamic~}}
|
||||||
|
public T GetOrDefaultAs<T>({{cs_define_type key_type}} key) where T : {{cs_define_type value_type}} => _dataMap.TryGetValue(key, out var v) ? (T)v : null;
|
||||||
|
public T GetAs<T>({{cs_define_type key_type}} key) where T : {{cs_define_type value_type}} => (T)_dataMap[key];
|
||||||
|
{{~end~}}
|
||||||
|
public {{cs_define_type value_type}} GetOrDefault({{cs_define_type key_type}} key) => _dataMap.TryGetValue(key, out var v) ? v : null;
|
||||||
|
public {{cs_define_type value_type}} Get({{cs_define_type key_type}} key) => _dataMap[key];
|
||||||
|
public {{cs_define_type value_type}} this[{{cs_define_type key_type}} key] => _dataMap[key];
|
||||||
|
|
||||||
|
public void Resolve(Dictionary<string, object> _tables)
|
||||||
|
{
|
||||||
|
foreach(var v in _dataList)
|
||||||
|
{
|
||||||
|
v.Resolve(_tables);
|
||||||
|
}
|
||||||
|
OnResolveFinish(_tables);
|
||||||
|
}
|
||||||
|
|
||||||
|
{{~else~}}
|
||||||
|
|
||||||
|
private readonly {{cs_define_type value_type}} _data;
|
||||||
|
|
||||||
|
public {{name}}(JsonElement _buf)
|
||||||
|
{
|
||||||
|
int n = _buf.GetArrayLength();
|
||||||
|
if (n != 1) throw new SerializationException(""table mode=one, but size != 1"");
|
||||||
|
_data = {{cs_define_type value_type}}.Deserialize{{value_type.bean.name}}(_buf[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{{~ for field in value_type.bean.hierarchy_export_fields ~}}
|
||||||
|
public {{cs_define_type field.ctype}} {{field.cs_style_name}} => _data.{{field.cs_style_name}};
|
||||||
|
{{~if field.ref~}}
|
||||||
|
public {{field.cs_ref_type_name}} {{field.cs_ref_var_name}} => _data.{{field.cs_ref_var_name}};
|
||||||
|
{{~end~}}
|
||||||
|
{{~end~}}
|
||||||
|
|
||||||
|
public void Resolve(Dictionary<string, object> _tables)
|
||||||
|
{
|
||||||
|
_data.Resolve(_tables);
|
||||||
|
OnResolveFinish(_tables);
|
||||||
|
}
|
||||||
|
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
partial void OnResolveFinish(Dictionary<string, object> _tables);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
");
|
||||||
|
var result = template.RenderCode(p);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static Template t_serviceRender;
|
||||||
|
public override string RenderService(string name, string module, List<DefTable> tables)
|
||||||
|
{
|
||||||
|
var template = t_serviceRender ??= Template.Parse(@"
|
||||||
|
using Bright.Serialization;
|
||||||
|
using System.Text.Json;
|
||||||
|
{{
|
||||||
|
name = x.name
|
||||||
|
namespace = x.namespace
|
||||||
|
tables = x.tables
|
||||||
|
}}
|
||||||
|
namespace {{namespace}}
|
||||||
|
{
|
||||||
|
|
||||||
|
public sealed partial class {{name}}
|
||||||
|
{
|
||||||
|
{{- for table in tables }}
|
||||||
|
public {{table.full_name}} {{table.name}} {get; }
|
||||||
|
{{-end}}
|
||||||
|
|
||||||
|
public {{name}}(System.Func<string, JsonElement> loader)
|
||||||
|
{
|
||||||
|
var tables = new System.Collections.Generic.Dictionary<string, object>();
|
||||||
|
{{- for table in tables }}
|
||||||
|
{{table.name}} = new {{table.full_name}}(loader(""{{table.json_output_data_file}}""));
|
||||||
|
tables.Add(""{{table.full_name}}"", {{table.name}});
|
||||||
|
{{-end}}
|
||||||
|
|
||||||
|
{{- for table in tables }}
|
||||||
|
{{table.name}}.Resolve(tables);
|
||||||
|
{{-end}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
");
|
||||||
|
var result = template.RenderCode(new
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
Namespace = module,
|
||||||
|
Tables = tables,
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue