游戏客户端,服务器的策划表格数据导出
将电子表格文件根据制定proto及字段转化规则,导出为Protobuf文本格式(*.pbt), json或lua格式
使用者通过读取Protobuf文本格式, json, lua等格式直接获取到所有的格式化数据
本转化器无需依赖vbs,vba.跨平台
直接输出基于Protobuf文本的格式化数据, 直接读取
支持lua, json格式输出, 无需解析,转换,直接读取
字段位置随意调整, 自动检查错误, 精确报错位置
充分利用CPU多核进行导出, 是已知的现有导出器中最快的
持续更新, 不断添加新功能, 提高工作效率
- Fairy in Wonderland https://itunes.apple.com/us/app/fairy-in-wonderland-parkour/id1128656892?l=zh&ls=1&mt=8
另2款使用tabtoy的游戏, 待发布
如果你的项目在使用tabtoy, 请联系我添加链接, 互相宣传
-
2011年: 一代导出器,基于VBA的表格内建导出器,速度慢,复用困难,容易错,不安全
-
2012年: 二代导出器,基于C++和Protobuf的导出器,内容格式与导出器混合编写,需要vbs导出csv,速度慢
-
2013年: 三代导出器,在二代基础上做到内容格式与导出器独立,但依然依赖csv前置导出,增加逗号分隔格子内容,导出速度慢
-
2015年: 四代导出器,基于Golang导出器,增加ID重复检查,数组格的多重写法, 支持a.b.c栏位导出, 导出速度大大提高
-
2016年: 五代导出器,在四代基础上重构,开源,支持并发导出,速度达到极致
前面多个版本都在本人项目中使用
53个Excel源文件, 格式xlsm, 大小3.8M
导出速度(硬件环境: i7-4790 8核+SSD)
-
9.4s 第四代导出器
-
4.9s 第五代导出器单线程
-
2.4s 第五代导出器
go get github.com/davyxu/tabtoy
go install github.com/davyxu/tabtoy
syntax = "proto3";
package test;
enum ActorType
{
Fighter = 0; // [tabtoy] Alias:"格斗士" #使用#号可在meta后方添加注释
Power = 21; // [tabtoy] Alias: "超能"
}
message Prop
{
int32 HP = 1;
float AttackRate = 2;
}
// 代表每一行对应的整个字段描述
message ActorDefine
{
// 唯一ID
int32 ID = 1; // [tabtoy] RepeatCheck: true #ID重复检查
// 角色名称
string Name = 5;
Prop Struct = 10;
repeated int32 BuffID = 20; // 重复字段可以用多列进行读取
// 角色类型
ActorType Type = 30;
repeated int32 SkillID = 40; // [tabtoy] String2ListSpliter: "," #使用,切割字符串
Prop StrStruct = 50; // [tabtoy] String2Struct: true
}
// 代表导出文件
message ActorFile
{
repeated ActorDefine Actor = 1; // 每一个元素代表电子表格的一行
}
- 通过protobuf的编译器protoc, 配合protoc-gen-meta插件导出带有meta信息的pb文件 protoc-gen-meta的获取: https://github.com/davyxu/pbmeta
..\proto\protoc.exe test.proto --plugin=protoc-gen-meta=..\..\..\..\..\bin\protoc-gen-meta.exe --proto_path "." --meta_out test.pb:.- 通过tabtoy读取meta信息及电子表格,指定输出文件夹,输出同名的pbt文件
..\..\..\..\..\bin\tabtoy.exe --mode=xls2pbt --pb=test.pb --outdir=. --fmt=pbt Actor.xlsx
导出文件样式
# Generated by github.com/davyxu/tabtoy
Actor {ID: 100 Name: "黑猫警长" Struct {HP: 100 AttackRate: 0.6} BuffID: 0 BuffID: 0 SkillID: 4 SkillID: 6 SkillID: 7 }
Actor {ID: 101 Name: "葫芦\n娃" Struct {HP: 10 AttackRate: 0.8} BuffID: 3 BuffID: 1 Type: Power SkillID: 1 }
Actor {ID: 102 Name: "舒\"克\"" Struct {HP: 10 AttackRate: 0.7} BuffID: 0 BuffID: 0 SkillID: 0 StrStruct {HP: 2} StrStruct {AttackRate: 0.5} StrStruct {HP: 3 AttackRate: 1}}
Actor {ID: 103 Name: "贝\n塔" Struct {HP: 205} BuffID: 0 BuffID: 0 SkillID: 0 }
Actor {ID: 104 Name: "邋遢大王" Struct {HP: 10 AttackRate: 1} BuffID: 0 BuffID: 0 SkillID: 0 }
proto文件格式范例参考test/test.proto
需要配合github.com/davyxu/pbmeta的protobuf插件protoc-gen-meta导出proto文件的meta信息
在proto的字段后方的注释中以[tabtoy]开头的注释将被理解为meta信息, 用于描述字段导出功能修饰 例如: // [tabtoy] RepeatCheck: true #ID重复检查
[tabtoy] 后方的格式为Protobuf Text, 使用#作为注释
具体的meta功能请参考后面的小节
详细例子参考: https://github.com/davyxu/tabtoy/blob/master/test/test.proto
默认导出pbt, 通过参数可以导出lua, json格式
参数: --fmt=pbt
Google Protobuf 官方支持的格式
可通过官方protobuf库读取,写入这种格式
与json区别在于:
json的字段名必须是带双引号, 且数组需要用[]圈住, 多重字段尾部必须加逗号
* 格式
key1: value1 key2: value2
冒号组合key,value, 空格分隔字段
\#作为注释
默认导出pbt
参数: --fmt=lua
导出以return开头的lua文件, 通过require的返回值回去表格table
兼容pbc格式, 枚举值以值名字字符串导出, 64位以字符串方式导出
为了方便lua导出文件的使用, 可以创建字段索引, 步骤如下, 参考test/test.proto:
为ActorDefine消息的ID字段和Name字段增加描述LuaMapper: true
参数: --fmt=json
标准json, 与proto定义的结构导出json相同
必须放在需要导出的Sheet的1,1位置
格式: ProtoTypeName: "package名.message名" RowFieldName: "导出字段"
Proto字段列, 必须放在第二行
用于描述字段功能, 必须放在第三行
从第四行开始, 一直到 第一列为空 时表示数据行结束, 后续行不再导出
当字段类型为结构体A, 实例名为a时, 若需要设置结构体内部的某字段类型B, 实例名为b时
只需要在Proto字段行中填写a.b即可
本功能仅限于非repeated字段
当字段类型为repeated类型时, 可将字段按同名方式切成多个字段导出
\r 和 \n 对应的转义符将被转成字符 \r 和 \n 在pbt加载时, 加载器会自动转回转义符
格式: Alias: "别名"
当枚举值字段meta信息中填写Alias: "别名"时
单元格值中即可填写原枚举的程序枚举类型, 也可以填写别名
格式: String2Struct: true
当字段meta信息中填写String2Struct: true时, 将把单元格的值理解为Protobuf Text格式并检查输出
支持在Struct中的字段添加Alias别名以使用别名方式的字段或中文字段描述单元格
范例步骤: 请参考test/test.proto
-
为Prop消息的需要的字段增加描述 AttackRate 为Alias:"攻击速率" ExType为Alias:"额外类型" 等
-
确认StrStruct字段拥有描述String2Struct: true
-
在StrStruct的单元格里填写 血量: 3 攻击速率: 1 额外类型:超能
-
导出测试
格式: RepeatCheck: true
当字段meta信息中填写RepeatCheck: true时, 将检查列中的本字段是否有重复字段(字符串方式)
格式: String2ListSpliter: ";"
当字段类型为repeated类型时, 且字段meta信息中填写String2ListSpliter: "分隔字符串"时
字段值会用"分隔字符串"切割并存储到repeated字段中
P.S. 建议分割字符串不要使用逗号. 电子表格中, 数字中带逗号表示数位分隔符, 会导致无法切割的问题
格式: DefaultValue: "xxx"
protobuf v3版本中去掉了DefaultValue支持, 在meta信息中添加DefaultValue可以在单元格为空时 自动填充为DefaultValue中的值, DefaultValue为字符串, 内部导出处理方式与单元格流程一致
-
建议每个proto文件对应1个电子表格(xls,xlsm),名字统一
例: Actor.proto 对应的电子表格名Actor.xls,最终导出文件为Actor.pbt
-
一个电子表格内的所有Sheet都是合并导出为一个pbt 例: Actor.xls中拥有名为ActorA ActorB的Sheet,最终导出的Actor.pbt中将包含ActorA和ActorB的数据
-
没有导出头(单元格1,1)的格子不会被导出
引用github.com/golang/protobuf库
例子:
func LoadPBTFile(filename string, msg proto.Message) error {
content, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
return proto.UnmarshalText(string(content), msg)
}
// 准备你的消息结构
var msg gamedef.YourMessage
// 直接读取pbt文件
LoadPBTFile( "Data.pbt", &msg )
步骤
-
使用github.com/davyxu/mergepbt 将多个pbt合并并转为二进制
-
使用protobuf-net库加载对应的二进制文件并读取
例如:
using (var stream = File.OpenRead(pathToPbt))
{
var yourmsg = ProtoBuf.Serializer.Deserialize<gamedef.ClientConfig>(stream);
stream.Close();
}
直接读取pbt格式
使用https://github.com/davyxu/ProtobufTextSerializer库直接加载
- 支持protobuf文本格式的语言可以直接读取pbt文件
例如: 官方包内的支持的C++, C#, Java, Golang等等
- 不支持文本格式的语言需要使用https://github.com/davyxu/mergepbt进行合并转换, 使用二进制读取
例如: Unity3D使用的protobuf-net
-
为什么输出文本格式,而不是二进制?
从设计角度: 文本只需要字段就可以导出, 而二进制需要复杂的二进制连接操作,设计复杂度较低
从使用角度: 文本调试,查看很方便
需要二进制导出请参见https://github.com/davyxu/mergepbt
-
C++和C#支持么?
C++用官方的Protobuf库可以读取导出格式, C#的protobuf-net无法读取, 需要自己根据你的消息格式转换文本到二进制才可读取
-
导出meta信息时, 多个proto文件写的批处理很长, 怎么办?
批处理的多行连接符是^, 例如
protoc a.proto ^
b.proto ^
c.proto
注意 空格不能少
-
为什么并发导出时, 日志顺序是乱的?
Visual Studio并发编译C++时也是乱的, 当然它可以调顺序模式
乱和速度不可兼得, 因为懒:)
不定期更新打包版本 https://github.com/davyxu/tabtoy/releases
想获取最新版请取最新代码编译
-
1.1 支持lua, json导出
-
1.0 基本功能
感觉不错请star, 谢谢!
博客: http://www.cppblog.com/sunicdavy
