xresloader转表工具

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表中类型调用

等级玩家升级经验体力上限玩家升级增加的体力值
idplayexpplayerLevelMaxEnergyplayerLevelAddEnergy
15012010
210012211
315012412

        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.xlsxplayerSheet3,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-8UTF-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中进行如下配置

等级玩家升级经验体力上限玩家升级增加的体力值
idplayexpplayerLevelMaxEnergyplayerLevelAddEnergy
1通用1级升级经验12010
2通用2级升级经验12211
315012412

以上只是很普通的示例以及衍生用法,还有很多特性扫一遍官方示例基本可以掌握七七八八,剩下的加以实操即可。

        接下来进行数据导出,这里虽然只有一个表,但是直接使用批量导出,单个表导出很简单,后续有需求可以直接使用一个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

CostValue@0-1000|2000-3000

10001

1

EN_CT_MONEY

10

10001

2

金币

50

        来来回回1万多字了,其实还有很多想要介绍,包括一些高级特性,后边自己稍微研究一下就明白了,记得看官方文档以及示例,结合使用,考虑到后续的内容,所以本章到现在就结束了~

         下期介绍另外一个自动化导表工具,目前在考虑结合ILRunTime进行实时全自动+运行时半自动讲解还是单拉出来直接讲解。

        如果对你有帮助,记得点赞~

        未经同意,请勿转载!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值