Compare commits

...
11 changed files with 132 additions and 33 deletions

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
github: [eigenein]
custom:
- 'https://www.paypal.me/eigenein'
- 'https://bunq.me/eigenein'
- 'https://money.yandex.ru/to/410011693851843'
+2 -2
View File
@@ -7,8 +7,8 @@ authors = ["Pavel Perestoronin <[email protected]>"]
readme = "README.md"
repository = "https://github.com/eigenein/wotbreplay-parser"
license = "MIT"
keywords = []
categories = []
keywords = ["world-of-tanks-blitz", "replay", "parser", "wargaming", "results"]
categories = ["parsing"]
[features]
default = []
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Pavel Perestoronin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+29
View File
@@ -8,6 +8,35 @@ World of Tanks Blitz replay parser in Rust.
![License: MIT](https://img.shields.io/crates/l/wotbreplay-parser)
[![docs.rs](https://img.shields.io/docsrs/wotbreplay-parser)](https://docs.rs/wotbreplay-parser)
## Quickstart
```rust
use std::fs::File;
use anyhow::Result;
use wotbreplay_parser::Replay;
use wotbreplay_parser::models::TeamNumber;
fn main() -> Result<()> {
let battle_results = Replay::open(File::open("tests/replays/battle_results.wotbreplay")?)?
.read_battle_results()?;
assert_eq!(battle_results.timestamp, 1670018359);
assert_eq!(battle_results.players.len(), 14);
assert_eq!(battle_results.players[0].account_id, 520886428);
assert_eq!(battle_results.players[0].info.nickname, "77mmmr");
assert_eq!(battle_results.players[0].info.team_number(), TeamNumber::Two);
assert_eq!(battle_results.players[0].info.platoon_id, None);
assert_eq!(battle_results.players[1].info.nickname, "SNAK_THE_RIPPER");
assert_eq!(battle_results.players[1].info.team_number(), TeamNumber::One);
assert_eq!(battle_results.players[1].info.platoon_id, Some(547466834));
Ok(())
}
```
## Replay structure
`*.wotbreplay` is a ZIP-archive containing:
+21 -4
View File
@@ -57,6 +57,9 @@ pub struct PlayerInfo {
/// Player's team assignment.
#[prost(enumeration = "TeamNumber", tag = "3")]
pub team_number: i32,
#[prost(string, optional, tag = "5")]
pub clan_tag: Option<String>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, prost::Enumeration)]
@@ -82,6 +85,10 @@ pub struct PlayerResults {
/// Extended player's results information.
#[derive(Message)]
pub struct PlayerResultsInfo {
/// Credits earned – without special awards and medals and premium account excluded.
#[prost(uint32, tag = "2")]
pub credits: u32,
/// Base XP (the total without multipliers).
#[prost(uint32, tag = "3")]
pub base_xp: u32,
@@ -98,9 +105,11 @@ pub struct PlayerResultsInfo {
#[prost(uint32, tag = "8")]
pub damage_dealt: u32,
/// TODO: distinguish the kinds of assisted damage.
#[prost(uint32, tag = "9")]
pub damage_assisted_1: u32,
/// TODO: distinguish the kinds of assisted damage.
#[prost(uint32, tag = "10")]
pub damage_assisted_2: u32,
@@ -113,18 +122,26 @@ pub struct PlayerResultsInfo {
#[prost(uint32, tag = "15")]
pub n_penetrations_received: u32,
/// TODO: distinguish earned vs seized.
#[prost(uint32, tag = "17")]
pub n_enemies_damaged: u32,
#[prost(uint32, tag = "18")]
pub n_enemies_destroyed: u32,
#[prost(uint32, tag = "32")]
pub victory_points_1: u32,
pub victory_points_earned: u32,
/// TODO: distinguish earned vs seized.
#[prost(uint32, tag = "33")]
pub victory_points_2: u32,
pub victory_points_seized: u32,
/// Player's account ID.
#[prost(uint32, tag = "101")]
pub account_id: u32,
/// Player's tank ID as per Wargaming.net API.
#[prost(uint32, tag = "103")]
pub tank_id: u32,
/// Rating for the Rating Battles.
///
/// Note, that this is **not** the game client's displayed rating.
+4 -2
View File
@@ -11,9 +11,11 @@ use crate::{Error, Result};
#[derive(Debug, Deserialize)]
pub struct BattleResultsDat(
/// No idea what this is.
u64,
/// My best guess – some kind of checksum,
/// but none of MD5, SHA1, SHA224, SHA256, SHA384, SHA512, nor CRC64 over the buffer have matched.
pub u64,
/// Protobuf-serialized battle results.
ByteBuf,
pub ByteBuf,
);
impl BattleResultsDat {
+6 -1
View File
@@ -19,10 +19,15 @@ impl<R: Read + Seek> Replay<R> {
/// Reads and parses the battle results from the replay.
pub fn read_battle_results(&mut self) -> Result<BattleResults> {
self.read_battle_results_dat()?.decode_battle_results()
}
/// Reads and parses the included `battle_results.dat`.
pub fn read_battle_results_dat(&mut self) -> Result<BattleResultsDat> {
let pickled_battle_results = self
.0
.by_name("battle_results.dat")
.map_err(Error::OpenBattleResultsFailed)?;
BattleResultsDat::from_reader(pickled_battle_results)?.decode_battle_results()
BattleResultsDat::from_reader(pickled_battle_results)
}
}
+44 -24
View File
@@ -1,32 +1,11 @@
use std::fs::File;
use anyhow::Result;
use wotbreplay_parser::models::TeamNumber;
use wotbreplay_parser::Replay;
#[test]
fn battle_results_ok() -> Result<()> {
let battle_results = Replay::open(File::open("tests/replays/battle_results_ok.wotbreplay")?)?
.read_battle_results()?;
assert_eq!(battle_results.timestamp, 1670018359);
assert_eq!(battle_results.players.len(), 14);
assert_eq!(battle_results.players[0].account_id, 520886428);
assert_eq!(battle_results.players[0].info.nickname, "77mmmr");
assert_eq!(battle_results.players[0].info.team_number(), TeamNumber::Two);
assert_eq!(battle_results.players[0].info.platoon_id, None);
assert_eq!(battle_results.players[1].info.nickname, "SNAK_THE_RIPPER");
assert_eq!(battle_results.players[1].info.team_number(), TeamNumber::One);
assert_eq!(battle_results.players[1].info.platoon_id, Some(547466834));
Ok(())
}
#[test]
fn player_results_ok() -> Result<()> {
let battle_results = Replay::open(File::open("tests/replays/player_results_ok.wotbreplay")?)?
let battle_results = Replay::open(File::open("tests/replays/player_results.wotbreplay")?)?
.read_battle_results()?;
let snak_the_ripper_info = &battle_results.player_results[3].info;
@@ -38,6 +17,9 @@ fn player_results_ok() -> Result<()> {
assert_eq!(snak_the_ripper_info.n_shots, 6);
assert_eq!(snak_the_ripper_info.n_hits_dealt, 5);
assert_eq!(snak_the_ripper_info.n_penetrations_dealt, 5);
assert_eq!(snak_the_ripper_info.tank_id, 5233);
assert_eq!(snak_the_ripper_info.n_enemies_damaged, 3);
assert_eq!(snak_the_ripper_info.n_enemies_destroyed, 2);
let zeekrab_info = &battle_results.player_results[10].info;
assert_eq!(zeekrab_info.base_xp, 701);
@@ -52,8 +34,12 @@ fn player_results_ok() -> Result<()> {
assert_eq!(zeekrab_info.n_hits_received, 3);
assert_eq!(zeekrab_info.n_penetrations_received, 3);
assert_eq!(zeekrab_info.n_non_penetrating_hits_received, 0);
assert_eq!(zeekrab_info.victory_points_1, 40);
assert_eq!(zeekrab_info.victory_points_2, 40);
assert_eq!(zeekrab_info.victory_points_earned, 40);
assert_eq!(zeekrab_info.victory_points_seized, 40);
assert_eq!(zeekrab_info.tank_id, 26657);
assert_eq!(zeekrab_info.credits, 37679);
assert_eq!(zeekrab_info.n_enemies_damaged, 2);
assert_eq!(zeekrab_info.n_enemies_destroyed, 1);
let balls_soup_info = &battle_results.player_results[8].info;
assert_eq!(balls_soup_info.damage_dealt, 2064);
@@ -64,6 +50,9 @@ fn player_results_ok() -> Result<()> {
assert_eq!(balls_soup_info.n_hits_received, 6);
assert_eq!(balls_soup_info.n_penetrations_received, 4);
assert_eq!(balls_soup_info.n_non_penetrating_hits_received, 2);
assert_eq!(balls_soup_info.tank_id, 4737);
assert_eq!(balls_soup_info.n_enemies_damaged, 4);
assert_eq!(balls_soup_info.n_enemies_destroyed, 1);
let ceaser_info = &battle_results.player_results[0].info;
assert_eq!(ceaser_info.damage_dealt, 919);
@@ -71,6 +60,37 @@ fn player_results_ok() -> Result<()> {
assert_eq!(ceaser_info.n_shots, 9);
assert_eq!(ceaser_info.n_hits_dealt, 6);
assert_eq!(ceaser_info.n_penetrations_dealt, 5);
assert_eq!(ceaser_info.n_enemies_damaged, 3);
assert_eq!(ceaser_info.n_enemies_destroyed, 0);
Ok(())
}
#[test]
fn clan_tags_ok() -> Result<()> {
let battle_results = Replay::open(File::open("tests/replays/player_results.wotbreplay")?)?
.read_battle_results()?;
assert_eq!(battle_results.players[1].info.clan_tag.as_deref(), Some("AN0NY"));
assert_eq!(battle_results.players[10].info.clan_tag.as_deref(), Some("BBS"));
assert_eq!(battle_results.players[11].info.clan_tag, None);
Ok(())
}
#[test]
fn victory_points_ok() -> Result<()> {
let battle_results = Replay::open(File::open("tests/replays/victory_points.wotbreplay")?)?
.read_battle_results()?;
assert_eq!(battle_results.player_results[1].info.victory_points_seized, 40);
assert_eq!(battle_results.player_results[1].info.victory_points_earned, 40);
assert_eq!(battle_results.player_results[3].info.victory_points_seized, 0);
assert_eq!(battle_results.player_results[3].info.victory_points_earned, 112);
assert_eq!(battle_results.player_results[5].info.victory_points_earned, 280);
assert_eq!(battle_results.player_results[5].info.victory_points_seized, 0);
Ok(())
}
Binary file not shown.