基于evido/wotreplay-parser开源项目的坦克世界游戏回放数据提取
Note: 2021/11/8 更新内容: 增加了对于函数的实现以及数据的提取. 如果后续有机会再来更新对于这些数据的分析
1. wotreplay-parser项目介绍:
https://github.com/evido/wotreplay-parser
wotreplay-parser项目是对*.wotreplay回放文件的第三方解码项目,可以用于将单场或者多场战斗进行可视化.
如:生成我方与敌方的走位路线图,交战位置的热力图,走位方式的热力图以及不同类型车辆位置图.
(图中为软件的PNG输出)
2. wotreplay-parser项目的使用:
使用wotreplay-parser需要准备Linux机器或者有qt运行支持的windows机器.
由于我对于qt库的使用不太了解,且在windows上运行时报错缺乏qt支持,于是我选择使用win10 ubuntu shell进行编译以及运行
首先,下载项目github中ubuntu分支的数据包,我是使用浏览器下载的zip文件,导入ubuntu之后进行解压(下载方法以及解压方法请查询其他教程)
注意: 该github的master分支中有所有依赖的安装命令, 而且由于master分支相比其他分支拥有 ext\cmake_modules\FindGD.cmake 文件, 所以可以编译成功. 对于其他分支, 可以通过将master中的该文件复制到对应文件夹进行编译, 以此避免出现找不到依赖的问题
注意,由windows直接导入Ubuntu会导致文件无使用权限,需要使用chmod进行设置权限.
编译: 进入项目文件夹,顺序使用 cmake . 以及 make 两条语句进行编译
默认情况下,由于程序使用的部分库没有进行安装,会导致cmake或make语句报错.此时可以查询相关的库的安装方法,使用Ubuntu自带的apt-get进行安装.
使用的库有:
- OpenSSL
- Boost (Complete)
- LibPNG
- LibXML2
- libjson-cpp (included in the source, from http://jsoncpp.sourceforge.net/)
- Intel Threading Building Blocks (Optional)
编译成功后可运行:
./wotreplay-parser --parse --root <working directory> --type <output type> --input <input file> --output <output file>
注意,只有<>内的部分可以选择,其他所有部分不可省略
<working directory>: 默认情况下为项目文件夹下的data文件夹.其中存放的是坦克世界的地图图片素材,坦克信息以及地图标识图片素材.这部分字段在运行程序的过程中不需要进行更改,除非用户手动更改了data文件夹的地址
<output type>: 程序输出类型,分为: png, json, team-heatmap, heatmap and class-heatmap. 开发者承诺的gif功能似乎有问题,无法运行.
<input file>: 可以填写*.wotreplay文件地址或者是包含*.wotreplay文件的文件夹地址
<output file>: 可以手动填写输出的文件名或者是输出的文件夹(在该情况下使用默认的输出名称). 根据开发者在项目仓库中的描述,可以省略 --output <output file> 字段,但是个人没有尝试过,毕竟这并不重要
3.个人再开发
开发目标: 使用软件中已经有的对于坦克世界回放文件的解码功能,将数据包中的数据提取成csv表格,以方便其用于机器学习或者是任何数据可视化项目.
3.1wotreplay-parser项目结构以及wotreplay文件结构
wotreplay-parser项目主要由三大块模块组成: (1).文件读取与解密模块 (2).数据整理与输出模块 (3).日志生成与GUI等模块
(1).文件读取与解密模块:
packet_reader_t类进行文件的读取以及分段,parser_t类进行数据的解密解压,game_t类对解密解压之后的数据进行结构化的保存,方便后输出生成器的调用
(2).数据整理与输出模块:
以writer_t作为总接口,下面实现png heatmap等实现类,基于game_t的数据进行整理,并将输出转化为对应文件格式
3).日志生成与GUI等模块:
这部分代码几乎对于再开发没有太大用处,本人也不了解相关的库,于是该部分代码被保持原样
wotreplay-parser项目UML(仅供参考)
wotreplay文件结构:
其中block 2在某些回放中存在,但是在部分回放中不存在. 可能与该回放是否记录游戏结束时的总结有关.
其中block1与block2是不加密的(使用utf-8进行编码,可直接使用文本编辑器浏览),
其中block1是json格式,由大括号开始与结尾. block2是数组形式,以']'结尾
但是block3是加密且压缩的,对应的解密与解压算法存在于parser类中
block3解密之后是游戏每个刷新时刻的package数据.
项目原作者在github中写道,回放有共12种数据包可以被读取,但是实际测试中,由于游戏更新以及游戏数据包格式的改变,其中仅有少数几种是保持有效的.
具体有效的包的数目与使用的项目版本以及游戏回放版本有关
3.2个人再开发
再开发的思路:在保证原有程序可安全编译的条件下插入个人的代码,绕过输出模块直接读取game类中的游戏数据包,并根据属性建立csv文件进行输出.
再开发的实现:
在main.cpp中:
int process_replay_file(...)包含了处理单个回放文件的全部流程.(处理文件夹的流程是int process_replay_directory(...)).
其中在第374行创建了game_t game对象用于存储回放数据
在第377行通过parser.parse(in, game); 对game对象进行输入.
在378行之后开始进入输出流程.
所以,在main.cpp中创建自己读取game对象的输出函数,并插入到第378行即可进行数据输出
代码实现:
使用循环遍历game.get_packets() ,调出每一个package对应的数据对象
使用package.get_properties()查询对于某个包可以调用的数据属性有哪些.
使用每个属性对应的数据接口(e.g. package.health() package.source())调出对应的属性值
将每个属性值根据其属性的顺序生成csv的数据表,最后使用iostream输出
由于程序开发完成的时间太过久远,做出的csv文件以及编辑后的项目源文件已经被清理,所以无法展示输出示例
根据开发完成后的笔记,总共12种数据包中只有health(),position(),turret_orientation(),tank_destroyed(),message()可以使用
具体的可使用的属性值与输入的回放文件对应的游戏版本有关.
如您有兴趣可自行尝试,
预计花费时间:2小时
/// 2021/11/8 静态兴趣来了重新把这个项目实现了一下
1 // 在main.cpp中创建以下函数. 2 // 插入到 process_replay_directory 或者 process_replay_file 中的 writer->update(game); 之后 3 // 其实插入到其他位置也是可以的, 只要可以取得输出目录以及game对象. 4 5 void csv_print(const wotreplay::game_t &game, std::string fileName){ 6 // return; 7 std::cout<<fileName<<std::endl; 8 std::ofstream ofs(fileName); 9 for (int j = 0; j <= 16; j++) 10 { 11 wotreplay::property_t property = static_cast<wotreplay::property_t>(j); 12 ofs<<print_helper(game.get_packets().at(0),property, true); 13 } 14 ofs<<std::endl; 15 for (auto &&i : game.get_packets()) 16 { 17 if (!i.has_property(wotreplay::property_t::position) || i.type()!=10) continue; 18 for (int j = 0; j <= 16; j++) 19 { 20 wotreplay::property_t property = static_cast<wotreplay::property_t>(j); 21 if(i.has_property(property)){ 22 ofs<<print_helper(i,property, false); 23 }else{ 24 // ofs<<","; 25 } 26 } 27 ofs<<std::endl; 28 } 29 ofs.close(); 30 } 31 32 std::string print_helper(const wotreplay::packet_t &packet, wotreplay::property_t property, bool is_header){ 33 std::string result = ""; 34 try 35 { 36 switch (property) 37 { 38 case wotreplay::property_t::clock: 39 if (is_header) 40 { 41 result = "clock"; 42 }else{ 43 result = std::to_string(packet.clock()); 44 } 45 break; 46 case wotreplay::property_t::position: 47 if (is_header) 48 { 49 result = "position"; 50 }else{ 51 result = 52 std::to_string(std::get<0>(packet.position()))+"_"+ 53 std::to_string(std::get<1>(packet.position()))+"_"+ 54 std::to_string(std::get<2>(packet.position())); 55 } 56 break; 57 case wotreplay::property_t::player_id: 58 if (is_header) 59 { 60 result = "player_id"; 61 }else{ 62 result = std::to_string(packet.player_id()); 63 } 64 break; 65 default: 66 break; 67 } 68 } 69 catch(const std::exception& e) 70 { 71 result = "error"; 72 } 73 if (result.length()>0) 74 { 75 result+=","; 76 } 77 return result; 78 }
附上对于这个项目的gdb调试方法:
> cmake -DCMAKE_BUILD_TYPE=Debug . && make # 进行debug编译 ... > gdbserver :1234 .../wotreplay-parser/bin/wotreplay-parser --parse --root .../wotreplay-parser/data --type png --input replay/xxxxx.wotreplay --output xxxxxx.png # 开启调试
进行VSCode设置: 断点调试永远的神!
1 { 2 // 使用 IntelliSense 了解相关属性。 3 // 悬停以查看现有属性的描述。 4 // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 "version": "0.2.0", 6 "configurations": [ 7 { 8 "name": "wot-replay-phaser", 9 "type": "cppdbg", 10 "request": "launch", 11 "program": ".../wotreplay-parser/bin/wotreplay-parser", 12 "args": [], 13 "stopAtEntry": false, 14 "cwd": ".../wotreplay-parser/bin/", 15 "environment": [], 16 "externalConsole": false, 17 "MIMode": "gdb", 18 "setupCommands": [ 19 { 20 "description": "为 gdb 启用整齐打印", 21 "text": "-enable-pretty-printing", 22 "ignoreFailures": true 23 } 24 ], 25 // "preLaunchTask": "C/C++: g++ 生成活动文件", 26 "miDebuggerPath": "/usr/bin/gdb", 27 "miDebuggerServerAddress": "localhost:1234" //根据自己的端口进行调整 28 } 29 ] 30 }
输出结果:
后续就是用Python_pandas处理以及绘图分析了.我就不详细写过程了
另外, 这里可以发现只能看到用户ID而不能看到玩家的车辆属性. 该部分内容是在回放文件头部的Json中. 手动将回放文件头部的Json截取出来或者在代码中创建自己的输出函数都可以. 因为这部分Json是没有加密的. 所以想怎么整都行, 我就不写了.
目前暂时没有对这些数据进行分析建模的打算. 如果大佬们有什么有意思的想法以及进行了什么建模. 欢迎来戳我!