惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Recent Commits to openclaw:main
Recent Commits to openclaw:main
GbyAI
GbyAI
Y
Y Combinator Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
aimingoo的专栏
aimingoo的专栏
H
Hackread – Cybersecurity News, Data Breaches, AI and More
云风的 BLOG
云风的 BLOG
博客园 - 【当耐特】
Hugging Face - Blog
Hugging Face - Blog
M
MIT News - Artificial intelligence
博客园_首页
B
Blog RSS Feed
Recorded Future
Recorded Future
N
Netflix TechBlog - Medium
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
罗磊的独立博客
Apple Machine Learning Research
Apple Machine Learning Research
D
DataBreaches.Net
IT之家
IT之家
The GitHub Blog
The GitHub Blog
P
Proofpoint News Feed
Microsoft Azure Blog
Microsoft Azure Blog
S
SegmentFault 最新的问题
宝玉的分享
宝玉的分享
T
The Blog of Author Tim Ferriss
B
Blog
The Cloudflare Blog
MyScale Blog
MyScale Blog
雷峰网
雷峰网
U
Unit 42
C
Check Point Blog
月光博客
月光博客
Blog — PlanetScale
Blog — PlanetScale
T
Tailwind CSS Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
大猫的无限游戏
大猫的无限游戏
D
Docker
Microsoft Security Blog
Microsoft Security Blog
WordPress大学
WordPress大学
L
LangChain Blog
F
Fortinet All Blogs
腾讯CDC
Martin Fowler
Martin Fowler
I
InfoQ
J
Java Code Geeks
博客园 - Franky
Engineering at Meta
Engineering at Meta
人人都是产品经理
人人都是产品经理
有赞技术团队
有赞技术团队
阮一峰的网络日志
阮一峰的网络日志

I'm OWenT

国产大模型(GLM 5.1、Kimi K2.6)真实场景效果和 Coding Plan 额度测试 新版本libatapp的连接管理——从etcd服务发现到拓扑驱动的自动重连 新版本libatbus的设计变更——从树形路由到拓扑驱动 Protobuf又一坑 - C++标准和ABI兼容性 AI真好用-给Blog主题统一加mermaid,chart.js,excalidraw,draw.io的多种引入方式支持 给内网部署Squid-通用HTTP下载缓存 UE使用CodeChecker和clang-tidy生成静态分析报告 找出UE的循环依赖 C++小协程栈和临时变量及作用域的栈溢出问题分析 游戏服务的可观测性能力建设(C++生态) 指标上报的多线程优化和多拉取源点优化 协程(libcopp)的Channel功能和CPU命中率优化 通用RPC代码生成器 实现strong_rc_ptr(比shared_ptr更快的引用计数智能指针) 手夯一个STL allocator和对象内存分析组件 std::condition_variable 的信号丢失问题 踩坑一处(GCC)STL std::async 实现BUG导致的crash问题 GCC 14的一个warning to error BUG 给xresloader(Excel导表工具)增强UE读表支持(包含蓝图,Blueprint) Opentelemetry社区在gRPC的几个链接问题(静态库和动态库混用,musl工具链,符号裁剪) Excel转表工具(xresloader)的新验证器(验证外部Excel和文本数据,唯一性和自定义规则) protobuf v22和gRPC v1.55版本升级的依赖变化和upb适配 关于protobuf近期版本(v20/v3.20+)和 gRPC v1.54版本在某些编译环境下的一些链接和编译问题 xresloader-Excel导表工具链的近期变更汇总 打通游戏服务端框架的C++20协程改造的最后一环 Opentelemetry-cpp的Logs模块标准更新(涉及近期版本:1.8-1.9的BREAK CHANGES) 给cmake-toolset和工具链(curl等)加HTTP/2和HTTP/3支持 又开新坑之 coredns 插件: nftables和filter 关于opentelemetry-cpp社区对于C++ Head Only组件单例和符号可见性的讨论小记 填个转表工具 xresloader 去年的坑(数组尾部裁剪) 集成 upb 和 lua binding 的踩坑小记 libcopp对C++20协程的接入和接口设计 再度优化GCC、LLVM、Clang、libc++、libc++abi等套件的构建脚本 游戏服务的分布式事务优化(二)- 事务管理 游戏服务的分布式事务优化(一)- Write Ahead Log(WAL) 模块 记录一些bazel适配用编译选项 测试现代化硬件C++浮点数性能和一致性 适配Boringssl和OpenSSL 3.0 近期cmake-toolset的一些适配问题 C++20 Text Formatting/fmtlib 适配问题小记 再次重构LLVM+Clang+libcxx+libc++abi+其他相关工具的构建流程 重构基于CMake的构建工具链 新版GCC和LLVM+Clang终于Release啦 折腾一下nftables下的双拨 [C++20] Module partitions和符号交叉引用(声明和实现分离) [Rust] 实现一个线程安全且迭代器可以保存的链表 基于protobuf的代码生成 几个使用protobuf中C++接口的Arena的坑 Amazon Aurora DB存储引擎论文阅读小记 近期对libatapp的一些优化调整(增加服务发现和连接管理,支持yaml等) 在游戏服务器中使用分布式事务 libcopp接入C++20 Coroutine和一些过渡期的设计 libatbus 的大幅优化 nftables初体验 容器配置开发环境小计 PALM Tree - 适合多核并发架构的B+树 - 论文阅读小记 跨平台协程库 - libcopp 简介 C++20 Coroutine 性能测试 (附带和libcopp/libco/libgo/goroutine/linux ucontext对比) 尝鲜Github Action 一些xresloader(转表工具)的改进 protobuf、flatbuffer、msgpack 针对小数据包的简单对比 协程框架(libcopp) 小幅优化 Excel转表工具(xresloader) 增加protobuf插件功能和集成 UnrealEngine 支持 Anna(支持任意扩展和超高性能的KV数据库系统)阅读笔记 C++20 Coroutine libcopp merge boost.context 1.69.0 Google去中心化分布式系统论文三件套(Percolator、Spanner、F1)读后感 Rust玩具-企业微信机器人通用服务 使用ELK辅助监控开发测试环境服务质量和问题定位 2018年的新通用伪随机数算法(xoshiro / xoroshiro)的C++(head only)实现 Webpack+vue+boostrap+ejs构建Web版GM工具 Rust的第二次接触-写个小服务器程序 理解和适配AEAD加密套件 atsf4g-co的进化:协程框架v2、对象路由系统和一些其他细节优化 协程框架(libcopp)v2优化、自适应栈池和同类库的Benchmark对比 可执行文件压缩 初识Rust 使用restructedtext编写xresloader文档 atframework的etcd模块化重构 C++的backtrace ECDH椭圆双曲线(比DH快10倍的密钥交换)算法简介和封装 protobuf-net的动态Message实现 pbc的proto3接入 atgateway内置协议流程优化-加密、算法协商和ECDH 整理一波软件源镜像同步工具+DevOps工具 Blog切换到Hugo libcopp v2的第一波优化完成 libcopp(v2) vs goroutine性能测试 libcopp的线程安全、栈池和merge boost.context 1.64.0 GCC 7和LLVM+Clang+libc++abi 4.0的构建脚本 libatbus的几个藏得很深的bug 用cmake交叉编译到iOS和Android 开源项目得一些小维护 atapp的c binding和c#适配 对象路由系统设计 2016年总结 近期的一个协程流程BUG 重写了llvm+clang+libc++和libc++abi的构建脚本 atsf4g完整游戏工程示例|I'm OWenT atframework基本框架已经完成|I'm OWenT
xresloader转表工具链增加了一些新功能(map,oneof支持,输出矩阵,基于模板引擎的加载代码生成等)
owent · 2020-08-29 · via I'm OWenT

blog-website

xresloader 是一组用于把Excel数据结构化并导出为程序可读的数据文件的导表工具集。它包含了一系列跨平台的工具、协议描述和数据读取代码。支持把Excel配置输出成 protobuf二进制、xml、json、lua、javascript、nodejs、msgpack、UE的Json格式及支持蓝图的代码、UE的Csv格式及支持蓝图的代码。

最近一段时间有一些其他项目组也用了 xresloader 工具链来执行转表。提了一些需求,我并且针对我们自己的项目需要耶新增了一系列功能。这里总结介绍一下吧。

Github: https://github.com/xresloader

文档: https://xresloader.atframe.work/

主要项目 说明 状态
xresloader 转表引擎 GitHub release (latest by date) GitHub Actions status GitHub code size in bytes GitHub repo size GitHub All Releases GitHub forks GitHub stars
xresconv-gui GUI客户端 GitHub release (latest by date) GitHub Actions status GitHub code size in bytes GitHub repo size GitHub All Releases GitHub forks GitHub stars
xresconv-cli 命令行客户端 GitHub release (latest by date) GitHub code size in bytes GitHub repo size GitHub forks GitHub stars
xresloader-docs 文档 GitHub code size in bytes GitHub repo size Build Status
xresconv-conf 转表配置规范 GitHub repo size

plain模式

为了方便某些特殊场景使用,从 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配置是如下形式:

配置ID Plain结构 Plain数组
id plain_msg plain_arr
101 101|1,2,3 7;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模式(需要 xresloader 2.7.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;
}

以下输入都是允许的:

配置ID Oneof结构
id reward
1001 msg|101;1,2,3
1002 数字类型|100
1003 13|Hello World
1004 enum_type|金币
1005 货币类型|EN_CT_DIAMOND

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

UE-JsonUE-Csv 输出的蓝图代码中,增加指示oneof分支的字段,便于对 oneof 输出的分支判断和反射使用。

更多详情见: 《文档 - Oneof/Union支持(需要 xresloader 2.8.0及以上)》

Map类型支持

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输入:

配置ID Map嵌套模式[0].key Map嵌套模式[0].value Map嵌套模式[1].key Map嵌套模式[1].value MapPlain模式
id test_map_is[0].key test_map_is[0].value test_map_is[1].key test_map_is[1].value test_map_sm
1001 10 Map嵌套模式[0].value 11 Map嵌套模式[1].value aa;111,112|特殊:字符;121,122
1002 20 Map嵌套模式[0].value 21 Map嵌套模式[1].value ba;211,212|特殊.字符;221,222
1003 30 Map嵌套模式[0].value 31 Map嵌套模式[1].value ca;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 属性指示类型名。

更多详情见: 《文档 - Map类型支持(需要 xresloader 2.9.0及以上)》

杂项功能和优化

  • 增加 --require-mapping-all 用于检查message中所有字段都被映射,用于检查配置遗漏
  • 增加protobuf插件 - org.xresloader.msg_require_mapping_all 可以设置某个message的所有字段必须被全部映射,用于检查配置遗漏
  • 增加protobuf插件 - org.xresloader.field_required 用于向proto3提供,proto2的 required 约束
  • 协议里直接配置 enum 类型也支持默认增加该类型的验证器
  • 文本输出的模式全部为有序输出,方便使用diff查看差异
  • 增加输出数据源和协议名,方便运行时自动化分析配置问题
  • 内置插件proto和官方proto。这样输入的pb文件不需要打包官方的proto描述和xresloader自带的插件和头的proto了。

基于模板引擎的加载代码生成

目前我们项目组开发了工具来自动生成加载代码,包括 C++luaC# 版本。支持 多索引多级索引多版本支持支持嵌入客户端引擎支持C++98 - C++20支持简单的分表分文件加载规则

比如:

syntax = "proto3";

import "xresloader.proto";
import "xresloader_ue.proto";

import "xrescode_extensions_v3.proto";

message role_upgrade_cfg {
    option (org.xresloader.ue.helper)       = "helper";
    option (org.xresloader.msg_description) = "Test role_upgrade_cfg with multi keys";

    option (xrescode.loader) = {
        file_path : "role_upgrade_cfg.bytes"
        indexes : {
            fields : "Id"
            index_type : EN_INDEX_KL // Key - List index: (Id) => list<role_upgrade_cfg>
        }
        indexes : {
            fields : "Id"
            fields : "Level"
            index_type : EN_INDEX_KV // Key - Value index: (Id, Level) => role_upgrade_cfg
        }
        tags : "client"
        tags : "server"
    };

    uint32 Id        = 1 [ (org.xresloader.ue.key_tag) = 1000 ];
    uint32 Level     = 2 [ (org.xresloader.ue.key_tag) = 1 ];
    uint32 CostType  = 3 [ (org.xresloader.verifier) = "cost_type", (org.xresloader.field_description) = "Refer to cost_type" ];
    int32  CostValue = 4;
    int32  ScoreAdd  = 5;
}

加载代码生成 - C++

生成的C++索引代码类似这样:

namespace excel {
    class config_set_role_upgrade_cfg {
    public:
        typedef const ::role_upgrade_cfg item_type;
        typedef ::role_upgrade_cfg proto_type;
        typedef std::shared_ptr<item_type> item_ptr_type;

        // 省略掉一些加载代码 ...

        // ------------------------- index: id -------------------------
    public:
        typedef std::vector<item_ptr_type> id_value_type;
        const id_value_type* get_list_by_id(uint32_t Id);
        item_ptr_type get_by_id(uint32_t Id, size_t index);
    private:
        const id_value_type* _get_list_by_id(uint32_t Id);
    public:
        typedef std::map<std::tuple<uint32_t>, id_value_type> id_container_type;
        const id_container_type& get_all_of_id() const;

    private:
        id_container_type id_data_;

        // ------------------------- index: id_level -------------------------
    public:
        typedef item_ptr_type id_level_value_type;
        id_level_value_type get_by_id_level(uint32_t Id, uint32_t Level);
        typedef std::map<std::tuple<uint32_t, uint32_t>, id_level_value_type> id_level_container_type;
        const id_level_container_type& get_all_of_id_level() const;

    private:
        id_level_container_type id_level_data_;
    };
}

可以用如下代码加载:

#include <cstdio>

#include "config_manager.h"
#include "config_easy_api.h"

int main() {
    // Initialize ....
    excel::config_manager::me()->init();

    // Call reload to generate a configure group
    excel::config_manager::me()->reload();

    // Now you can load data by easy api or config_manager's raw API
    auto cfg = excel::get_role_upgrade_cfg_by_id_level(10001, 3); // using the Key-Value index: id_level
    if (cfg) {
        printf("%s\n", cfg->DebugString().c_str());
    }
    return 0;
}

加载代码生成 - Lua

生成的lua索引代码类似这样:

module("DataTableCustomIndex", package.seeall)
--
-- generated by xrescode on 2020-05-07 18:12:18, please don't edit it
--

-- ======================================== role_upgrade_cfg ========================================
role_upgrade_cfg = {
    -- require("DataTableService").Get("role_upgrade_cfg"):GetByIndex("id", "Id")
    { indexName = "id", filePath = "role_upgrade_cfg.bytes", options = { isList = true, allowNotFound = true }, keys = { "Id" } },
    -- require("DataTableService").Get("role_upgrade_cfg"):GetByIndex("id_level", "Id", "Level")
    { indexName = "id_level", filePath = "role_upgrade_cfg.bytes", options = { isList = false, allowNotFound = true }, keys = { "Id", "Level" } },
}

然后可以用这样加载:

-- We will use require(...) to load DataTableService53,DataTableCustomIndex53 and custom data files, please ensure these can be load by require(FILE_PATH)
-- Assuming the generated lua files by xresloader is located at ../../../xresloader/sample/proto_v3
package.path = '../../../xresloader/sample/proto_v3/?.lua;' .. package.path
local excel_config_service = require('DataTableService53')

-- Set logger
-- excel_config_service:OnError = function (message, index, indexName, keys...) end

excel_config_service:ReloadTables()

local role_upgrade_cfg = excel_config_service:Get("role_upgrade_cfg")
local data = role_upgrade_cfg:GetByIndex("id_level", 10001, 3) -- using the Key-Value index: id_level
for k,v in pairs(data) do
    print(string.format("%s=%s", k, tostring(v)))
end

-- We can also use DataTableService.GetCurrentGroup(self) and DataTableService.GetByGroup(self, group, loader_name) to support multi-version loader
local current_group = excel_config_service:GetCurrentGroup()
local role_upgrade_cfg2 = excel_config_service:GetByGroup(current_group, "role_upgrade_cfg")
local data2 = role_upgrade_cfg:GetByIndex("id", 10001) -- using the Key-List index: id
print("=======================")
for _,v1 in ipairs(data2) do
    print(string.format("\tid: %s, level: %s", tostring(v1.Id), tostring(v1.Level)))
    for k,v2 in pairs(v1) do
        print(string.format("\t\t%s=%s", k, tostring(v2)))
    end
end

加载代码生成 - C#

同样,也支持生成C#代码,生成的代码就不贴了,加载代码如下:

using System;
using excel;
class Program {
    static void Main(string[] args) {
        ConfigSetManager.Instance.Reload();
        // The C# configSet now generated by Singleton Classes.
        // For the multi ConfigGroup management may be added when need.
        var table = config_set_role_upgrade_cfg.Instance.GetByIdLevel(10001, 3);
        if (table != null) {
            Console.WriteLine(table.ToString());
        }
    }
}

目前的C#的代码生成的版本还不支持多版本并存。

更多详情见工具的代码仓库: https://github.com/xresloader/xres-code-generator