xresloader是一组把Excel数据结构化并且导出数据文件的工具,自身包含了跨平台的工具以及协议描述和数据读取代码,当然也可以使用我们自身的方式进行读取和自动化协议描述。
主要功能特点:
-
跨平台(java 1.8 or upper)
-
Excel => protobuf/msgpack/lua/javascript/json/xml
-
完整支持协议结构,包括嵌套结构和数组嵌套
-
同时支持protobuf proto v2 和 proto v3
-
支持导出proto枚举值到lua/javascript代码和json/xml数据
-
支持导出proto描述信息值到lua/javascript代码和json/xml数据(支持自定义插件,方便用户根据proto描述自定义反射功能)
-
支持导出 UnrealEngine 支持的json或csv格式,支持自动生成和导出 UnrealEngine 的
DataTable
加载代码 -
支持别名表,用于给数据内容使用一个易读的名字
-
支持验证器,可以在数据里直接填写proto字段名或枚举名,或者验证填入数据的是否有效
-
支持通过protobuf协议插件控制部分输出
-
支持自动合表,把多个Excel数据表合并成一个输出文件
-
支持公式
-
支持oneof,支持plain模式输入字符串转为数组或复杂结构,支持map
-
支持空数据压缩(裁剪)或保留定长数组
-
支持基于正则表达式分词的字段名映射转换规则
-
支持设置数据版本号
-
Lua输出支持全局导出或导出为
require
模块或导出为module
模块。 -
Javascript输出支持全局导出或导出为
nodejs
模块或导出为AMD
模块。 -
提供CLI批量转换工具(支持python 2.7/python 3 @ Windows、macOS、Linux)
-
提供GUI批量转换工具(支持Windows、macOS、Linux)
-
CLI/GUI批量转换工具支持include来实现配置复用
官方文档:快速上手 — xresloader-document https://xresloader.atframe.work 文档
环境
1.JRE/JDK 8或以上,安装好cmd检查是否安装成功。
2.protoc工具,可以自己编译也可以从下方下载预编译好的protoc,我们这里使用proto3。
地址:https://github.com/xresloader/xresloader/releases
如果是使用Unity的同学,要注意自己的Unity版本,其中会用到.Net RunTime,2018.4.3以内的Unity版本,即使可以切换Scripting Runtime Version为.Net 4.x也没有预编译的proto版本高,所以2018.4.3以内的Unity版本建议下载protoc-3.10.0,感兴趣的同学可以自行编译查看。
3.下载工具集
3.1:转表工具
地址:https://github.com/xresloader/xresloader/releases
不使用插件的情况下,下载jar直接使用即可,如果后期要使用插件,下载protocols.zip,我们这里使用xresloader-x.x.x.jar。
3.2:批量转表工具
批量转表工具分为两种:
一、命令行批量转表工具(xresconv-cli)
二、GUI批量转表工具 (xresconv-gui)
大家根据需要使用即可,我们这里先把两者都下载下来,两个工具的使用都较为简单,后边会 大 概讲一下。
3.3:批量配置模板
地址:https://github.com/xresloader/xresconv-conf
里边包含一个sample、sample_include、README、LICENSE。
README和LICENSE不用说,大家最好养成习惯去看README,每一次编辑都是有意义的。
sample是批量导表时需要的配置,后边会用到。
sample_include是用于从sample里拿到所有配置,修改部分配置之后把输出文件内容转换成 lua代码,这样我们开发的时候查看数据就会很方便。
环境配置完成之后,先来看一张流程图,作为使用者需要了解的。
转表引擎架构以及流程图
简单概述一下本文的操作流程,因为处于讲解阶段,所以步骤都是单独概述,后期可以根据个人需求进行自动化。
生成数据文件
1.编辑Excel数据以及编写对应的协议描述文件。
2.组织文件夹结构。
3.将协议描述文件生成为.pb文件。
4.编辑批量转表xml(sample)配置中的路径,格式等信息。
5.使用gui批量转表工具生成对应的数据,.bin/.cpp/.json/.xml/.lua/.js/UE的CSV/Msgpack打包的二进制数据,当然也支持将一些公用proto转出,比如枚举,属性,buff等。
生成加载代码
首先,对于c++、Lua、C# 推荐使用xres-code-generator生成解析代码,这也是官方所推荐的,大概步骤是在proto文件中声明加载器和索引类型,这个下边会详细讲解。注意:如果使用xres-code-generator来进行生成则配置python环境,推荐大家使用python3。
如果想要手动解析也没有问题,数据的生成是按照xresloader_datablocks的结构,常规情况下都是使用二进制数据,其他格式一看就知道怎么回事了,所以也用二进制数据举例,先用xresloader_datablocks解析二进制文件,得到封装后的结构体,然后使用proto解析每条data_block,这个在示例里也有很清晰的对应关系,一看便知,然后data_block里会有协议描述里的message,这时候就可以随便使用了。
这里我会先用gui工具生成二进制文件,然后使用xres-code-generator工程生成对应的C#加载代码来进行测试
1.将编写好的协议描述文件拷贝到xres-code-generator工程中
2.(可选)编辑cs.mako模板文件,加载代码会根据这个模板文件进行生成,使用默认模板也可以
3.编写对应的shell脚本,使用find_porotoc.py找到我们对应平台的protoc,将我们所有的协议描述文件生成一个.pb文件,然后使用xrescode-gen.py,输入cs.mako模板文件输出到我们指定的文件夹中
流程和大概思路明了之后,开始配置(想了想还是回来提及一下,文下所有的衍生用法只是方便大家加深理解并且明白xresloader的多样性以及可扩展性,并不止局限于下边的几个例子)
配置Excel数据以及编写对应的协议描述文件
1.新建一个playerlevel.xlsx的Excel,Sheet1名为playerSheet,Sheet2名为scheme_playerlevel,playerSheet填写数据,scheme_playerlevel填写协议描述文件以及Sheet的关联,还有一些转出规则设置。
衍生用法:Sheet1>=玩家等级表,Sheet2==与Sheet1协议描述的对应关系等,Sheet3>=玩家星级表,Sheet4==与Sheet3协议描述的对应关系等...,以此类推,一个类型的表分为不同的页签放在一张Excel里,当然也可以分为多个Excel。
这里与以往不同的地方在于不需要在Excel里配置类型,会直接体现在对应的协议描述文件中,这样兼容以及扩展性会大大增加,比如设置字段别名,Excel表中类型调用
等级 | 玩家升级经验 | 体力上限 | 玩家升级增加的体力值 |
id | playexp | playerLevelMaxEnergy | playerLevelAddEnergy |
1 | 50 | 120 | 10 |
2 | 100 | 122 | 11 |
3 | 150 | 124 | 12 |
scheme_playerlevel页签稍后会有讲解。
2.编写对应的协议描述文件。
新建一个kind_test.proto文件。
syntax = "proto3";
message player_cfg {
int32 id = 1;
int32 playexp = 2;
int32 playerlevelmaxenergy = 3;
int32 playerleveladdenergy = 4;
}
3.配置proto以及Excel的关联
编辑scheme_playerlevel页签
字段 | 简介 | 主配置 | 次配置 | 补充配置 | 说明 |
DataSource | 配置数据源(文件路径,表名) | playlevel.xlsx | playerSheet | 3,1 | 次配置为表名,补充配置为数据起始位置(行号, 列号) |
编程接口配置 | |||||
ProtoName | 协议描述名称 | player_cfg | |||
OutputFile | 输出文件 | player_cfg.bin | |||
KeyRow | 字段名描述行 | 2 | |||
KeyCase | 字段名大小写 | 小写 | 大写/小写/不变 | ||
KeyWordSplit | 字段名分词字符 | 可留空 | |||
KeyPrefix | 字段名固定前缀 | ||||
KeySuffix | 字段名固定后缀 | ||||
KeyWordRegex | 分词规则(判断规则,移除分词符号规则,前缀过滤规则) | [A-Z_\$ \t\r\n] | [_\$ \t\r\n] | [a-zA-Z_\$] | 正则表达式 |
Encoding | 编码转换 | UTF-8 | UTF-8/GB18030/GBK等所有Java默认带的编码格式 注: Google的protobuf库的代码里写死了UTF-8(2.6.1版本),故而该选项对Protobuf无效 |
这个配置比较丰富,也有很多衍生用法,DataSource为数据源,主配置为文件名+后缀,此配置为Sheet名,补充配置为从几行几列开始和proto顺序对应,ProtoName为对应的协议描述文件中的message名称,OutputFile为输出的数据,剩下的配置行也是一目了然,正常情况下都走默认配置即可。
衍生用法:
1.多个数据Sheet对应一个scheme_Sheet,比如:多个相同字段的数据Sheet或者互相包含字段的数据Sheet,不同作用的数据Sheet共用一个协议描述文件中的Message,前提是字段名大部分相同,这里也可以引出派生Excel数据,多张Sheet派生自一个Sheet,最后统一生成一份数据,并且不需要担心多余的字段也会被生成出来,只会生成本Sheet有的字段,这时候不同的使用者会用于不同的逻辑
2.多个数据Sheet引用一个元数据Sheet,这个Sheet可以是其他文件夹其他表中的Sheet,比如有一个Sheet>=macro,内容如下:
键 | 值 |
通用1级升级经验 | 50 |
通用2级升级经验 | 100 |
在scheme_playerlevel中DataSource行下增加一行,关键字MacroSource,该关键词为元数据,主配置为macro所在的Excel文件名,如果在本Excel中则是playlevel.xlsx,次配置为macro,补充配置为2,1,这样直接可以在playlevel.xlsx-->playerSheet中进行如下配置
等级 | 玩家升级经验 | 体力上限 | 玩家升级增加的体力值 |
id | playexp | playerLevelMaxEnergy | playerLevelAddEnergy |
1 | 通用1级升级经验 | 120 | 10 |
2 | 通用2级升级经验 | 122 | 11 |
3 | 150 | 124 | 12 |
以上只是很普通的示例以及衍生用法,还有很多特性扫一遍官方示例基本可以掌握七七八八,剩下的加以实操即可。
接下来进行数据导出,这里虽然只有一个表,但是直接使用批量导出,单个表导出很简单,后续有需求可以直接使用一个bat或者shell脚本简单实现下即可。
批量转表工具读取协议描述文件的方式是通过我们提供的.pb文件,所以这一步需要把所有的协议描述文件生成一个.pb文件,这里只有一个那么只生成一个即可,当然现在只是尽可能展开讲述,后续可以全部做成自动化~~
在步骤3.3批量配置模板中,得到了sample.xml,这个是批量转表的配置文件,开始编辑它,如下是我的配置信息
<?xml version="1.0" encoding="UTF-8"?>
<!-- <?xml-stylesheet type="text/xsl" href="helper/view.xsl"?> -->
<root>
<!-- <include desc="可以包含其他文件配置,然后本文件里的配置将会覆盖或合并配置,相对于当前xml的目录">sample.xml</include> -->
<global>
<work_dir desc="工作目录">../xresloader_test/sample</work_dir>
<xresloader_path desc="xresloader地址">../xresloader-2.11.0.jar</xresloader_path>
<proto desc="协议类型,-p选项">protobuf</proto>
<output_type desc="输出类型,-t选项,支持多个同时配置多种输出">bin</output_type>
<output_type desc="多种输出时可以额外定义某个节点的重命名规则" rename="/(?i)\.bin$/\.json/">json</output_type>
<output_type desc="多种输出时可以额外定义某个节点的重命名规则" rename="/(?i)\.bin$/\.xml/">xml</output_type>
<!-- output_type 里的class标签对应下面item里的class标签,均可配置多个,多个用空格隔开,任意一个class匹配都会启用这个输出 -->
<proto_file desc="协议描述文件,work_dir目录下,-f选项">proto_v3/kind_test.pb</proto_file>
<output_dir desc="输出目录,-o选项">../sample-data</output_dir>
<data_src_dir desc="数据源目录,-d选项"></data_src_dir>
<data_version desc="数据版本号,留空则自动生成">1.0.0.0</data_version>
<rename desc="重命名规则,正则表达式:/搜索模式/替换内容/,对应xresloader的-n选项, 如果在output_type里设置了rename,以output_type里的rename为准,否则使用这里的全局配置" placeholder="/(?i)\.bin$/\.json/"></rename>
<java_option desc="java选项-最大内存限制2GB">-Xmx2048m</java_option>
<java_option desc="java选项-客户端模式">-client</java_option>
<default_scheme name="KeyRow" desc="默认scheme模式参数-Key行号">2</default_scheme>
<default_scheme name="MacroSource" desc="默认scheme模式参数-Key行号"></default_scheme>
<!--<default_scheme name="UeCg-CsvObjectWrapper" desc="Ue-Csv输出的包裹字符">{|}</default_scheme>-->
</global>
<groups desc="分组信息">
<group id="client" name="客户端"></group>
<group id="server" name="服务器"></group>
</groups>
<category desc="类信息">
<tree id="all_cats" name="ExcelData">
<tree id="player" name="角色配置"></tree>
</tree>
</category>
<list>
<item file="playlevel.xlsx" scheme="scheme_playerlevel" name="升级" cat="player" class="client"></item>
</list>
<gui>
<set_name desc="这个脚本用于修改树形节点的显示数据,便于策划核对具体的表名">
if (item_data.file) {
item_data.name += " (" + item_data.file.match(/([^.]+)\.\w+$/)[1] + ")"
}
</set_name>
<on_before_convert name="转表开始前事件" type="text/javascript" timeout="15000" description="事件执行结束必须调用resolve(value)或reject(reason)函数,以触发进行下一步">
// 这里可以执行nodejs代码,比如下面是Windows平台执行 echo work_dir
var os = require("os");
var spawn = require("child_process").spawn;
if (os.type().substr(0, 7).toLowerCase() == "windows") {
var exec = spawn("cmd", ["/c", "echo " + work_dir], {
cwd: work_dir,
encoding: 'utf-8'
});
exec.stdout.on("data", function(data) {
log_info(data);
});
exec.stderr.on("data", function(data) {
log_error(data);
});
exec.on("error", function(data) {
log_error(data.toString());
resolve();
// reject("执行失败" + data.toString());
});
exec.on("exit", function(code) {
if (code === 0) {
resolve();
} else {
resolve();
// reject("执行失败");
}
});
} else {
resolve();
}
</on_before_convert>
<on_after_convert name="转表完成后事件" type="text/javascript" timeout="60000" description="事件执行结束必须调用resolve(value)或reject(reason)函数,以触发进行下一步">
// 同上
alert_warning("自定义转表完成后事件,可以执行任意nodejs脚本");
resolve();
</on_after_convert>
<script
name="delaycall"
type="text/javascript">
if (data.running) {
reject("上一次未完成");
} else {
data.running = true;
var left_times = 5;
function counter() {
if (left_times > 0) {
log_notice(`定时器计数: ${left_times}`);
left_times -= 1;
require("timers").setTimeout(function(){
counter();
}, 1000);
return;
}
log_notice("定时器结束");
resolve();
}
counter();
}
</script>
<script name="自定义脚本" type="text/javascript">
// 同上
data.call_times = (data.call_times || 0) + 1;
alert_warning("自定义脚本,可用于自定义按钮");
log_notice(`自定义脚本:\n可用于自定义按钮 - notice日志(${data.call_times})`);
log_warning("自定义脚本\n可用于自定义按钮 - warning日志");
// resolve();
data.running = false;
resolve();
</script>
</gui>
</root>
接下来是我的目录结构
header是引擎使用的结构,生成的数据结构都是按照xresloader_datablocks结构体来生成。
header地址https://github.com/xresloader/xresloader/tree/master/header
sample是我们的工作目录
数据表我放在了sample文件夹下
proto_v3文件夹下是对应的所有协议描述,以及生成的.pb文件
sample_data文件夹存放导出的数据
接下来就是sample.xml以及在3.1步骤得到的批量转表工具jar
准备好之后打开下载的GUI批量导表工具xresconv-gui.exe
点击全部选中,点击开始转换,就可以在sample_data文件夹下看到我们导出的数据,我用的是多种输出类型,可以简单观察下xml和json的数据格式。
接下来进行代码加载,这一步可以使用手动解析,也可以使用自动生成解析代码,如果使用自动解析代码需要多操作一步,不过后续自动化工具也可以处理,这里我写了一个简单的自动化工具进行实现,这里还是使用手动方式进行操作,方便理解。
打开通过上方生成加载代码得到的xres-code-generator工程,或者直接从下方链接下载
GitHub - xresloader/xres-code-generatorContribute to xresloader/xres-code-generator development by creating an account on GitHub.https://github.com/xresloader/xres-code-generator 简单说下目录结构以及划分,当然可以自行整理或者拆分
3rd_party是mako以及生成proto相关必要文件的python脚本
pb_extension是proto相关必要文件以及用于我们自定义协议文件import并且option的xrescode_extensions_v3.proto文件和对应的python脚本,对import和option不太了解的同学可以去百度一下,后续有时间的话我会出一个详细的mac以及win双平台文章,最近给自己计划的东西挺多,下午有点累,通过观察xrescode_extensions_v3不难猜测到后续的流程,接着分析,里边首先实现了google.protobuf包里MessageOptions的扩展,然后包装了一层外部信息,比如路径,多集合,存储结构,标签等。
sample文件夹是放置自己写的协议描述文件以及转表引擎依赖的协议文件,生成code的脚本也可以放在这里。
template文件夹是存放模板的地方,使用的是mako模板,有需求直接修改模板即可。
tools顾名思义就是工具了,里边存放了find_protoc.py,看下里边的内容就明白了。
xrescode-utils里边存放了一些不同语言的类型接口,方便使用
开始操作
把之前编辑好的协议描述文件拷贝到sample/proto文件夹下
编辑kind_test协议描述文件
syntax = "proto3";
import "xresloader.proto";
import "xresloader_ue.proto";
//导入提供的结构proto
import "xrescode_extensions_v3.proto";
message role_cfg {
//声明loader
option (xrescode.loader) = {
//这里实际加载时会用该路径去加载,可以在模板里拼路径也可以在这里填写,如果这里写热更不好处理。
file_path : "role_cfg.bin"
//下边两个indexes结构是定义了转出的code脚本里数据存储方式,以及键值
indexes : {
fields : "id"
index_type : EN_INDEX_KL // Key - List index: (Id) => list<role_cfg>
}
indexes : {
fields : "id"
fields : "playexp"
index_type : EN_INDEX_KV // Key - Value index: (Id, Level) => list<role_cfg>
}
};
int32 id = 1;
int32 playexp = 2;
int32 playerlevelmaxenergy = 3;
int32 playerleveladdenergy = 4;
int32 cardexp = 5;
}
然后使用sample_gen生成即可,里边会生成多个平台,lua、cpp、c#,一个main脚本,main脚本只是示例,后边删掉即可,包括模板的使用,各个文件的输入输出都很清晰,把用到的留下即可,想了想在Unity里使用也没什么特别的,我把工程以及Excel表直接放在了Assets平级,对了,最后读取的时候把Google.Protobuf的包导进来,这个都知道吧,要注意上边说的proto版本的问题,也可以从C#包管理器直接把包以及相关依赖下载下来。
这里贴几个高级特性
配置ID | Plain结构 | Plain数组 |
---|---|---|
id | plain_msg | plain_arr |
101 | 101|1,2,3 | 7;8;9 |
配置ID | Oneof结构 |
---|---|
id | reward |
1001 | msg|101;1,2,3 |
1002 | 数字类型|100 |
1003 | 13|Hello World |
1004 | enum_type|金币 |
1005 | 货币类型|EN_CT_DIAMOND |
角色ID | 等级 | 货币类别 | 消耗值 |
---|---|---|---|
Id | Level | CostType | |
10001 | 1 | EN_CT_MONEY | 10 |
10001 | 2 | 金币 | 50 |
来来回回1万多字了,其实还有很多想要介绍,包括一些高级特性,后边自己稍微研究一下就明白了,记得看官方文档以及示例,结合使用,考虑到后续的内容,所以本章到现在就结束了~
下期介绍另外一个自动化导表工具,目前在考虑结合ILRunTime进行实时全自动+运行时半自动讲解还是单拉出来直接讲解。
如果对你有帮助,记得点赞~
未经同意,请勿转载!