跳到主要内容

高级功能

文本替换(别名/宏)

为了便于理解,我们支持配置一组别名的表。在 MacroSource 中,主配置为文件名,次配置为表明,补充配置为Key-Value的开始行号和列号。比如:

字段简介主配置次配置补充配置说明
MacroSource文本宏数据源(文件路径,表名)资源转换示例.xlsxmacro2,1次配置为表名,补充配置为数据起始位置(行号, 列号)

这时候我们会认为在文件 资源转换示例.xlsxmacro 表中。从第 2 行开始,第 1 列为别名的Key,第 2 列为别名的Value。

这样我们在执行转表的读取数据时候,会尝试去这里查找是否又它的别名,如果有,则直接转换成Value。

比如 xresloader sampleupgrade_10001 表的 CostType 这一列是整数类型,但是我们可以配置成 游戏币 。就是因为我们在 macro 表中配置了。

游戏币10001

多表数据合并

如果Excel的多个表的结构相同(列对应的字段相同)。则我们可以通过配置多个 DataSource 来让 xresloader 对多个表进行数据合并。这样我们可以把数据按类型分布在几个表中并在转换的时候最后合并。

详见 xresloader sample资源转换示例.xlsxscheme_upgradeupgrade_10001upgrade_10002 表。

数据验证器

xresloader 提供了一个基于协议描述得高级功能- 数据验证器 。用于限制输入数据的范围。 数据验证器 的使用方法是在Excel的字段名后面跟 @ 符号,然后输入协议名称或者数字范围 A-B ,多个验证器可以用 | 隔开。 或者也可以通过协议插件来设置验证器(详见: 下面插件章节)。这样在数据转出的时候转表工具会检查数据的合法性。

可用的验证器语法如下:

  • 函数: InText("文件名"[, 第几个字段[, \"字段分隔正则表达式\"]]) : 从文本文件(UTF-8编码),可以指定读第几个字段和用于字段分隔的正则表达式
  • 函数: InTableColumn("文件名", "Sheet名", 从第几行开始, 从第几列开始) : 从Excel数据列读取可用值,指定数据行和数据列
  • 函数: InTableColumn("文件名", "Sheet名", 从第几行开始, KeyRow, KeyValue) : 从Excel数据列读取可用值,指定数据行并通过某一行的的值获取数据列
  • 函数: Regex("正则表达式") : 验证匹配正则表达式(>=2.21.0版本)
  • 函数: InMacroTable("文件名", "Sheet名", 从第几行开始, 第几列是映射Key, 第几列是映射Value) : 从Excel里读取别名映射,指定数据行和别名映射Key和别名映射Value的列号(>=2.20.0版本)

    类似于Macro表,但是可以把这个验证器配置在指定字段或Excel列中,仅对指定字段或Excel列生效。

  • 函数: InMacroTable("文件名", "Sheet名", 从第几行开始, KeyRow, 映射Key字段名, 映射Value字段名) : 从Excel里读取别名映射,指定数据行并通过某一行的值别名映射Key和别名映射Value的列(>=2.20.0版本)

    类似于Macro表,但是可以把这个验证器配置在指定字段或Excel列中,仅对指定字段或Excel列生效。 具体可参考 xresloader/sample/custom_validator.yaml 内的 custom_rule6 和 xresloader/sample/proto_v3/kind.proto 内的 field_alias_message 配置。

  • 函数: And("子验证器", ...) : 必须同时满足多个子验证器(>=2.21.0版本)
  • 函数: Or("子验证器", ...) : 必须满足任一子验证器(>=2.21.0版本)
  • 函数: Not("子验证器", ...) : 必须不满足所有子验证器(>=2.22.2版本)
  • 函数: InValues(值, ...) : 必须满足候选值之一(>=2.22.2版本)
  • 自定义验证器名(通过 --validator-rules 加载)
  • 协议类型(对应protobuf的message里的每个field,excel里可以填field number或者field name)
  • 枚举类型(对应protobuf的enum里的每个number,excel里可以填enum number或者enum name)
  • 值范围: A-B(比如 0-1234 )或 >=A(比如 >=1234 )或 <=A(比如 <=1234 )或 >A(比如 >1234 )或 <A(比如 <1234
  • 多个验证器可以使用“或”符号(|)分隔,任意一个验证器满足条件即可视作合法,例如 100-200|2000-3000|test_msg_verifier

自定义验证器的配置格式(YAML):

validator:
- name: "validator name"
rules:
- validator_rule1
- validator_rule2
- ...

举例如下:

角色ID等级货币类别消耗值
角色ID等级货币类别消耗值
IdLevelCostTypeCostValue@0-1000 |2000-3000
100011
1000121000150

上面这个表,如果 消耗值 这一列出现了[0, 1000]和[2000-3000]以外的值,转表工具会转表不通过并予以提示。

还有一个特殊的用法是,比如我们有技能要对单位的属性加成。然后我们定义单位属性的proto如下:

message unit_attribute {
int32 hp = 1;
int32 mp = 2;
int32 power = 3;
}

message skill_effect {
int32 id = 1;
int32 level = 2;
int32 func_type = 3;
int32 attr_type = 4;
int32 value = 5;
}

然后我们可以定义技能功能表如下:

技能ID等级功能类别属性
技能ID等级功能类别属性
idlevelfunc_typeattr_type@unit attributevalue
200011hp100
2000121001hp200

使用 skill_effect 转出如上的表, 属性 这个字段的验证器设为了 unit_attributeattr_type 的类型是int32。 这时在转出数据的时候,转出的数据是 unit_attribute.hp 的字段编号 1

Protobuf 插件支持

项目中可以导入 xresloader-protocol/common 目录, 然后通过导入 xresloader-protocol/core/extensions/v2xresloader-protocol/core/extensions/v3 中的相应proto文件,就可以获得额外的插件扩展支持。

> 注意: 使用插件功能时 生成pb的时候也要导入插件的proto文件和protobuf官方include目录里的 google/protobuf/descriptor.proto 文件。

Protobuf插件 - Message插件

插件名称类型插件功能
插件名称类型插件功能
org.xresloader.msg_descriptionstring消息体描述信息,会写入输出的header中和代码中
org.xresloader.msg_require_mapping_allbool设置message的所有字段必须被全部映射
org.xresloader.msg_separatorstringPlain模式字段分隔符,可指定多个,用于在一个单元格内配置复杂格式时的分隔符列表,默认值: ,;|
org.xresloader.ue.helperstring生成UE Utility代码的类名后缀
org.xresloader.ue.not_data_tablebool生成UE Utility代码时,不生产加载代码,这用于带name字段的依赖类型
org.xresloader.ue.default_loaderenum生成UE Utility代码时,控制单独的Message是否开启默认Loader(版本>=v2.13.1) 可选项: EN_LOADER_MODE_DEFAULT; EN_LOADER_MODE_ENABLE; EN_LOADER_MODE_DISABLE
org.xresloader.ue.include_headerstring生成UE Utility代码时,额外附加包含文件(版本>=v2.13.1)

比如 xresloader/sample/proto_v3/kind.proto 里, arr_in_arr_cfg 配置了相关字段,会影响到一些输出。

Protobuf插件 - Field插件

插件名称类型插件功能
org.xresloader.field_descriptionstring字段描述信息,会写入输出的header中和代码中
org.xresloader.validatorstring字段描述信息,会写入输出的header中和代码中(版本>=2.14.0-rc2)
org.xresloader.verifierstring(废弃,请使用 org.xresloader.validator)字段描述信息,会写入输出的header中和代码中
org.xresloader.field_unique_tagstring唯一性检测Tag,所有tag相同的字段会组合到一起检查唯一性(版本>=2.14.0-rc2)
org.xresloader.map_key_validatorstring用于Map类型Key的验证器,可填范围(low-high),message名,enum名。多个由 `
org.xresloader.map_value_validatorstring用于Map类型Value的验证器,可填范围(low-high),message名,enum名。多个由 `
org.xresloader.field_not_nullbool如果配置了字段映射,忽略此项为空的行(版本>=2.14.0-rc2)
org.xresloader.field_aliasstring字段别名,可用于验证器和Excel中直接填别名,2.14.0-rc2版本后允许多个
org.xresloader.field_ratioint32数值放大倍数,转出数值=Excel内数值*field_ratio。比如设为1000时,如果Excel里填的是1.05,转出的数据是 1050
org.xresloader.field_separatorstring
org.xresloader.field_requiredbool设置字段为 required ,用于向proto3提供,proto2的 required 约束
org.xresloader.field_origin_valuestring当前字段类型为Timestamp或Duration时且转换过程发生数据转换时,此扩展允许把原始数据写入指定字段。(版本>=2.12.0) 目标字段必须是string类型且repeated属性和当前字段保持一致
org.xresloader.field_allow_missing_in_plain_modeboolPlain模式下设置此字段可选,如果未设置则使用默认值(版本>=2.16.0)
org.xresloader.field_list_strip_optionenum给单个字段设置数组裁剪,可选值(版本>=2.18.0) + LIST_STRIP_DEFAULT : 默认值,使用 --list-strip-*/--list-keep-empty 控制,未设置则是裁剪全部空值 ) + LIST_STRIP_NOTHING: 不裁剪数据,相当于 --list-keep-empty + LIST_STRIP_TAIL: 裁剪尾部空值,相当于 --list-strip-empty-tail + LIST_STRIP_ALL: 裁剪全部空值,相当于 --list-strip-all-empty
org.xresloader.field_list_min_sizestring给单个字段设置数组最小长度,输入字符串:<N>或<枚举名>(版本>=2.18.0)
org.xresloader.field_list_max_sizestring给单个字段设置数组最大长度,输入字符串:<N>或<枚举名>(版本>=2.18.0)
org.xresloader.field_list_strict_sizebool设置单个字段设置数组严格长度要求,即不自动补全最小长度,而是报错。:false/true(默认值: false, 版本>=2.18.0)
org.xresloader.field_tagstring设置字段Tag,配合 --ignore-field-tags 选项可用于跳过某些数据。(版本>=2.19.0)
org.xresloader.ue.key_tagint32生成UE代码时,如果需要支持多个Key组合成一个Name,用这个字段指定系数(必须大于0)
org.xresloader.ue.ue_type_namestring生成UE代码时,如果指定了这个字段,那么生成的字段类型将是 TSoftObjectPtr<ue_type_name> , 并且支持蓝图中直接引用
org.xresloader.ue.ue_type_is_classbool生成UE代码时,如果这个字段为true,那么生成的字段类型将是 TSoftClassPtr<ue_type_name> , 并且支持蓝图中直接引用
org.xresloader.ue_origin_type_namestring设置输出UE代码的原始类型(版本>=2.14.0-rc1)
org.xresloader.ue_origin_type_default_valuestring设置输出UE代码的原始类型的默认值(版本>=2.14.0-rc1)

比如我们定义单位属性的proto如下:

import "xresloader.proto";

message unit_attribute {
int32 hp = 1 [(org.xresloader.field_alias) = "生命"];
int32 mp = 2 [(org.xresloader.field_alias) = "魔力"];
int32 power = 3 [(org.xresloader.field_alias) = "力量"];
}

message skill_effect {
int32 id = 1;
int32 level = 2;
int32 func_type = 3;
int32 attr_type = 4;
int32 value = 5;
}

然后我们可以在Excel表中使用别名:

技能ID等级功能类别属性
技能ID等级功能类别属性
idlevelfunc_typeattr_type@unit attributevalue
200011生命100
2000121001生命200

Protobuf插件 - EnumValue插件

插件名称类型插件功能
插件名称类型插件功能
org.xresloader.enumv_descriptionstring枚举值描述信息,会写入输出的header中和代码中
org.xresloader.enum_aliasstring枚举值别名,可用于验证器和Excel中直接填别名,2.14.0-rc2版本后允许多个

比如 xresloader/sample/proto_v3/kind.proto 里, role_upgrade_cfg 内的 CostType 这一列配置验证器引射到协议的 cost_type 和 协议描述字段。

sample/quick_start/sample-conf/kind.proto
syntax = "proto3";

import "xresloader.proto";
// xresloader的发布页面 https://github.com/xresloader/xresloader/releases 下载
// protocols.zip ,即可获取xresloader.proto

enum cost_type {
EN_CT_UNKNOWN = 0;
EN_CT_MONEY = 10001 [ (org.xresloader.enum_alias) = "金币" ];
EN_CT_DIAMOND = 10101 [ (org.xresloader.enum_alias) = "钻石" ];
}

message role_upgrade_cfg {
uint32 Id = 1;
uint32 Level = 2;
uint32 CostType = 3 [
(org.xresloader.validator) =
"cost_type", // 这里等同于在Excel中使用 @cost_type 标识
(org.xresloader.field_description) = "Refer to cost_type"
];
int32 CostValue = 4;
int32 ScoreAdd = 5;
}

然后,我们就可以按如下方式配消耗类型:

角色ID等级货币类别消耗值
角色ID等级货币类别消耗值
IdLevelCostTypeCostValue@0-1000 |2000-3000
100011EN_CT_MONEY10
100012金币50

Protobuf插件 - Oneof插件(2.8.0版本及以上)

插件名称类型插件功能
org.xresloader.oneof_descriptionstringoneof描述信息,可能会写入输出的header中和代码中
org.xresloader.oneof_separatorstring
org.xresloader.oneof_not_nullstring如果配置了字段映射,忽略此项为空的行(版本>=v2.14.0-rc2)
org.xresloader.oneof_allow_missing_in_plain_modeboolPlain模式下设置此字段可选,如果未设置则使用默认值 (版本>=2.16.0)
org.xresloader.oneof_tagstring设置字段Tag,配合 --ignore-field-tags 选项可用于跳过某些数据。(版本>=2.16.0)

仅导出部分字段

如果我们需要给客户端和服务器读取同一张Excel表里的不同字段的数据,只要proto不一样即可。对于proto中不存在的字段,我们在转换的时候会忽略掉。 即,我们可能会有一个 role_serverrole_client 。这两个数据结构不一样,但指向同一个数据源。

批量转表的include标签

公式支持

xresloader 支持公式功能,但是不建议使用跨文件公式。是因为有些平台里,文件的引用可能会使用绝对路径,这时候如果改变一个文件中的值会影响另一个文件。 而另一个文件计算公式的时候读取失败,则会用之前的数据缓存(Excel中对所有公式的计算结果有缓存)。这时候数据可能滞后,但是是没有提示的。可能会引起困惑。

定长数组

详见 数据类型说明-定长数组 <data-types-stable-array> 章节。

Plain模式(需要 xresloader 2.7.0及以上)

为了方便某些特殊场景使用,从 xresloader 2.7.0版本开始,我们开支支持Plain模式。 Plain模式的配置方式允许把数字和字符串数组和整个message配置在一个单元格里,多个元素或者多个字段按分隔符分割。分隔符支持多个候选项,实际执行会采用按输入的字符串中,第一个找到的候选项。 默认的分隔符候选项是 ,;|

Plain模式不需要额外配置,当数组元素没有配置下标或者配置的映射字段直接指向一个message时,将自动使用Plain模式解析。

比如对于以下协议:

message cfg {
int32 id = 1;
plain_message plain_msg = 2;
repeated int32 plain_arr = 3;
}

message plain_message {
int32 id = 1;
repeated int32 param = 2;
}

如果Excel配置是如下形式:

配置IDPlain结构Plain数组
配置IDPlain结构Plain数组
idplain_msgplain_arr
101101|1,2,37;8;9

那么对于 plain_msg 字段输入的字符串是 101|1,2,3 ,第一个 | 会作为 plain_msg 的字段分隔符, , 会作为 plain_msg.param 的数组分隔符。 而对于 plain_arr 字段输入的字符串是 7;8;9 , ; 会作为数组分隔符。

如果想要指定自定义分隔符,特别是对 repeated message 要区分message的分隔符和数组的分隔符,可以使用使用 org.xresloader.field_separator 插件和 org.xresloader.msg_separator 插件。 需要注意的是,对于数组(repeated)的字段,字段分隔符仅接受通过 org.xresloader.field_separator 指定,而非数组的复杂数据结构(非repeated message) org.xresloader.field_separator 插件和 org.xresloader.msg_separator 都可以用于指定分隔符。

同时,在Plain模式中,message字段解析是严格按照配置的field number的顺序。

更多详情请参考 xresloader samplearr_in_arr 表,对应协议是 xresloader/sample/proto_v3/kind.proto 中的 message arr_in_arr_cfg

UE-CsvUE-Json 输出的Plain模式需要 xresloader 2.8.0及以上。

Oneof/Union支持(需要 xresloader 2.8.0及以上)

xresloader 对Oneof的支持和Plain模式类似,并且只能通过Plain模式一样的方法配置,可以使用 org.xresloader.oneof_separator 插件指定自定义分隔符。

Oneof/Union支持的配置方法是直接在Excel字段映射中配置oneof的名字。输入字符串中第一组为字段的名字、数字标识(field number)或别名,第二组为对应的类型的Plain模式输入。比如:

// 常量类型
enum cost_type {
EN_CT_UNKNOWN = 0;
EN_CT_MONEY = 10001 [(org.xresloader.enum_alias) = "金币"];
EN_CT_DIAMOND = 10101 [(org.xresloader.enum_alias) = "钻石"];
}

message cfg {
int32 id = 1;
oneof reward {
plain_message msg = 11 [ (org.xresloader.field_alias) = "嵌套结构" ];
int64 user_exp = 12 [ (org.xresloader.field_alias) = "数字类型" ];
string note = 13 [ (org.xresloader.field_alias) = "描述文本" ];
cost_type enum_type = 14 [ (org.xresloader.field_alias) = "货币类型" ];
}
}

message plain_message {
int32 id = 1;
repeated int32 param = 2;
}

以下输入都是允许的:

配置IDOneof结构
配置IDOneof结构
idreward
1001msg|101;1,2,3
1002数字类型|100
100313|Hello World
1004enum_type|金币
1005货币类型|EN_CT_DIAMOND

需要特别注意的是,和Plain模式一样,message字段解析是严格按照配置的field number的顺序,如果message里有嵌套的oneof,那么oneof的输入位置是第一个相关字段的位置,并且该oneof里后续的字段不需要配置。

更多详情请参考 xresloader sampletest_oneof 表,对应协议是 xresloader/sample/proto_v3/kind.proto 中的 message event_cfg

Map类型支持(需要 xresloader 2.9.0及以上)

xresloader 2.9.0 版本开始,我们支持使用 protobuf 内置的map类型。map类型的数据输入配置和数组类似,与其不同的是,我们增加了内置的 keyvalue 字段用于通过标准模式指定元素的 keyvalue。 当然我们也可以使用Plain模式的输入。比如以下的协议:

message dep2_cfg {
uint32 id = 1;
string level = 2;
}

message arr_in_arr_cfg {
option (org.xresloader.ue.helper) = "helper";
option (org.xresloader.msg_description) = "Test arr_in_arr_cfg";

uint32 id = 1 [ (org.xresloader.ue.key_tag) = 1, (org.xresloader.field_description) = "This is a Key" ];
map<int32, string> test_map_is = 7;
map<string, dep2_cfg> test_map_sm = 8 [ (org.xresloader.field_separator) = "|" ];
}

我们接受如下的Excel输入:

配置IDMap嵌套模式[0].keyMap嵌套模式[0].valueMap嵌套模式[1].keyMap嵌套模式[1].valueMapPlain模式
配置IDMap嵌套模式[0].keyMap嵌套模式[0].valueMap嵌套模式[1].keyMap嵌套模式[1].valueMapPlain模式
idtest_map_is[0].keytest_map_is[0].valuetest_map_is[1].keytest_map_is[1].valuetest_map_sm
100110Map嵌套模式[0].value11Map嵌套模式[1].valueaa;111,112|特殊:字符;121,122
100220Map嵌套模式[0].value21Map嵌套模式[1].valueba;211,212|特殊.字符;221,222
100330Map嵌套模式[0].value31Map嵌套模式[1].valueca;311,312|cb;321,322

对于 UE-CsvUE-Json 模式的输出,我们会输入如下的代码:

USTRUCT(BlueprintType)
struct FArrInArrCfg : public FTableRowBase
{
GENERATED_USTRUCT_BODY()

// Start of fields
/** Field Type: STRING, Name: Name, Index: 0. This field is generated for UE Editor compatible. **/
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "XResConfig")
FName Name;

// This is a Key
/** Field Type: INT, Name: Id, Index: 1 **/
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "XResConfig")
int32 Id;

/** Field Type: MESSAGE, Name: TestMapIs, Index: 7 **/
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "XResConfig")
TMap< int32, FString > TestMapIs;

/** Field Type: MESSAGE, Name: TestMapSm, Index: 8 **/
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "XResConfig")
TMap< FString, FDep2Cfg > TestMapSm;
};

特别的对于 xml 类型的输出,由于map中的key的数据可能会不符合 xml 的tag的规则,所以我们对于map输出的数据中 tagName 采用类型名, 即 string , int32 , int64 。 然后增加 key 属性用于指示map中key的内容,增加 type 属性指示类型名。

更多详情请参考 xresloader samplearr_in_arr 表,对应协议是 xresloader/sample/proto_v3/kind.proto 中的 message arr_in_arr_cfg

使用 CallbackScript 处理数据(需要 xresloader 2.13.0及以上)

xresloader 2.13.0 版本开始,我们通过支持指定一个javascript脚本来修改生成的数据(通过 -m CallbackScript=脚本路径 ,参考: data-mapping-available-options )。

CallbackScript指向的javascript脚本中,需要满足已下条件:

  • 可使用 gOurInstance 访问数据源接口( DataSrcImpl.getOurInstance()
  • 可使用 gSchemeConf 访问数据转换配置接口( SchemeConf.getInstance()
  • 提供 function initDataSource() 函数,将在切换数据源时触发(文件名或sheet名)。
  • 提供 function currentMessageCallback(originMsg, typeDesc) 函数,将在切换数据源时触发(文件名或sheet名)。
    • originMsg 为原始数据结构的 HashMap 结构
    • typeDesc 为数据类型描述信息, org.xresloader.core.data.dst.DataDstWriterNode.DataDstTypeDescriptor 结构 更多详情请参考 xresloader sampleprocess_by_script1process_by_script2 表,还有 cb_script.js 文件。

多字段复制(需要 xresloader 2.14.0-rc1及以上)

可以在Excel KeyRow 指向的列里填写多个由 , 分隔的字段名,这样在转出的时候会把数据分别填充到这些字段里。比如:

message level_data_cfg {
uint32 level = 1;
uint32 exp = 2;
}

message level_up_cfg {
option (xrescode.loader) = {
file_path: "level_up.bytes"
indexes: { fields: "id" fields: "level" index_type: EN_INDEX_KV }
tags: "client"
tags: "server"
};

uint32 id = 1;
uint32 level = 2;
level_data_cfg data = 3;
}
角色ID等级经验
角色ID等级经验
idlevel,data.leveldata.exp
1000110

剔除Excel误操作带来的空数据行(需要 xresloader 2.14.0-rc2及以上)

我们可以通过 org.xresloader.field_not_null 插件和 org.xresloader.oneof_not_null 插件来用以忽略Excel中指定数据为空的数据行。 特别是对Excel误操作(比如漏删除空单元格,不小心设置了某个空数据行的单元格格式)。比如:

message level_up_cfg {
uint32 id = 1 [ (org.xresloader.field_not_null) = true ];
uint32 level = 2;
}
角色ID等级备注
角色ID等级备注
idlevel
100011
2此行会被忽略

唯一性检测(需要 xresloader 2.14.0-rc2及以上)

我们可以通过 org.xresloader.field_unique_tag 插件来设置唯一性检测标签。可多个。 对于相同唯一性检测标签的所有字段组合,只能出现一次。否则转换过程就会报错。比如:

message level_up_cfg {
uint32 id = 1 [ (org.xresloader.field_unique_tag) = "id_level" ];
uint32 level = 2 [ (org.xresloader.field_unique_tag) = "id_level" ];
}

角色ID等级备注
角色ID等级备注
idlevel
100011
100012
100011此行会冲突

通过自定义验证器在复用验证器规则组合(需要 xresloader 2.14.0-rc3及以上)

自定义验证器主要用于重复使用一些复杂组合的验证规则。比如我们配置奖励表,要求奖励必须是某个虚拟的道具ID(对应protobuf的枚举类型),或者在道具表中,或者在邮件表中,或者在商城表中等等。 每一个要配置奖励的地方都去单独写这么长的验证规则,一方面不好看,另一方面后续增加新类型维护起来非常容易出错。于是我们现在提供了一个自定义验证器的功能。

首先是增加了 --validator-rules 参数用于告诉 xresloader 去哪里读取自定义验证器,自定义验证器配置是一个 YAML 文件,格式如下:

validator:
- name: "validator name"
description: "(可选)描述"
version: 0 # 版本,从 2.20.0 版本开始支持
mode: or # 模式: or, and, not 。从 2.22.0 版本开始支持, 默认为 or
rules:
- validator_rule1
- validator_rule2
- ...

为了降低错误配置,我们会检测验证器的环形依赖。但是为了降低不必要的检测开销,我们仅仅在第一次使用这个验证器时才会做检查。

比如我们配置验证器:

validator:
# com.struct.battle.config.proto
- name: "UESourceAbilitySet_ue_source_id"
description: "UE局内 AbilitySet资源 值校验"
rules:
- InText("UeSource_AbilitySet.txt", 3)

- name: "ExcelAffixCountRandomPool_affix_count_pool_id"
description: "Affix.xlsx|词条数量随机池表|affix_count_pool_id 值校验"
rules:
- InTableColumn("Affix.xlsx", "词条数量随机池表", 3, 2, "affix_count_pool_id")

验证器检查不通过的一个示例如下(还包含一个唯一性检查报错):

image

我们也可以通过函数验证器或者 mode 参数来控制组合关系。 比如如果我们想配置一个字段必须符合枚举类型 package_name.cost_type ,但不允许0(通常遗漏配置会转出默认值0)和1。 有如下两中方式:

方式一:

validator:
- name: "CustomValidCostType"
mode: "and"
rules:
- package_name.cost_type
- Not(InValues(0, 1))

方式二:

validator:
- name: "CustomValidCostType"
rules:
- And(package_name.cost_type, Not(InValues(0, 1)))

全局配置/行列翻转(需要 xresloader 2.23.0及以上)

某些场景中,我们希望拿一个表维护全局配置。每行一个字段,并且限定数据提取范围。 此时我们可以用行列翻转( --transpose-data-source )和 限制数据源结束行列的功能。

比如我们在 资源转换示例.xlsx 文件里有 global_settings 表:

标识字符串值类型描述
test_duration130dgoogle.protobuf.Duration时间周期, 单位: w/weeks,d/days,h/hours,m/minutes,s/seconds,ms/milliseconds,us/microseconds,ns/nanoseconds
test_duration230msgoogle.protobuf.Duration时间周期, 单位: w/weeks,d/days,h/hours,m/minutes,s/seconds,ms/milliseconds,us/microseconds,ns/nanoseconds
test_timestamp2009-07-05 15:25:00google.protobuf.Timestamp时间点, 格式: 年-月-日 时:分:秒 表示系统时区时间, 年-月-日 时:分:秒Z 表示UTC时区时间, 年-月-日 时:分:秒+|-时:分 表示自定义时区时间
mail_max_count_per_major_type100int用户邮件每种大类的最大数量
i18n_system_admin系统管理员string系统管理员昵称
test_plain_msg1-2test_msg_verifierPlain模式Message
test_arr721,722,12|10001
821,822,货币类型|EN_CT_DIAMOND
921,822,描述文本|数组嵌套one of
event_rule_itemPlain模式Message数组,嵌套one of
test_repeated_timestamp2009-07-05 15:25:00
2009-07-05 15:25:00Z
2009-07-05 15:25:00+04:00
array:google.protobuf.Timestamp
timezone_base_timestamp2025-01-06 00:00:00+08:00google.protobuf.Timestamp基准时间用于结算跨周和跨天,必须是一周的第一天的零点时间(比如周一或者周日的东八区零点)
test_plain_enum_array金币,金币,钻石array:enum
test_standard_msg.id537
test_standard_msg.level648
test_map_is[0].key70map<int32, string>map简单测试
test_map_is[0].valueMap简单测试.value
test_map_is[1].key71
test_map_is[1].valueMap简单测试.value
test_map_sm[0].keyaamap<string, dep2_cfg>map嵌套测试
test_map_sm[0].value811,812
test_map_sm[1].key特殊:字符
test_map_sm[1].value821,822

然后有proto文件:

message global_settings {
google.protobuf.Duration test_duration1 = 1;
google.protobuf.Duration test_duration2 = 2;
google.protobuf.Timestamp test_timestamp = 3;
int32 mail_max_count_per_major_type = 4;
string i18n_system_admin = 5;
test_msg_verifier test_plain_msg = 6 [ (org.xresloader.field_separator) = "&" ];
repeated event_rule_item test_arr = 7 [ (org.xresloader.field_separator) = "\n" ];
repeated google.protobuf.Timestamp test_repeated_timestamp = 8 [ (org.xresloader.field_separator) = "\n" ];
google.protobuf.Timestamp timezone_base_timestamp = 9;
repeated cost_type test_plain_enum_array = 10;
map<int32, string> test_map_is = 11;
map<string, dep2_cfg> test_map_sm = 12 [ (org.xresloader.field_separator) = "|" ];
dep2_cfg test_standard_msg = 13;
}

我们可以在参数里增加下面参数把这个表数据转入一个 global_settings 的message结构中。

  • --transpose-data-source : 开启行列翻转,此时 KeyRow 指向列号。
  • -m "KeyRow=1" : 从第一列读字段映射的Key。
  • -m "DataSource=${workspaceFolder}/sample/资源转换示例.xlsx|global_settings|2,2,0,2" : 限定从第2行第2列开始读数据,到第2列截止。不限定截止行。