基于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是没有加密的. 所以想怎么整都行, 我就不写了.

 

目前暂时没有对这些数据进行分析建模的打算. 如果大佬们有什么有意思的想法以及进行了什么建模. 欢迎来戳我!

 

posted @ 2020-11-28 22:22  NoobSir  阅读(687)  评论(4编辑  收藏  举报