Compare commits

...
35 Commits
Author SHA1 Message Date
eigenein 14148c7410 Break: add initial meta.json support 2023-04-24 22:38:23 +02:00
eigenein 903cd00f32 Break: support arena_unique_id 2023-04-24 22:13:03 +02:00
eigenein e0ce6a22b1 Opt: add the docstrings and TODO 2023-04-20 01:24:07 +02:00
eigenein fb9beb5ecc Fix: remove the explicit cargo login call 2023-04-17 00:16:23 +02:00
eigenein 06de455795 Fix: workflow status badge 2023-04-17 00:11:52 +02:00
eigenein ac256d4c9d New: add Author::team_number 2023-04-17 00:05:03 +02:00
eigenein e853224017 Break: rename parse into from_buffer 2023-04-16 23:09:05 +02:00
eigenein 04420da334 New: impl TryInto<BattleResults> for BattleResultsDat 2023-04-16 23:05:41 +02:00
eigenein cd933f6584 Opt: chores 2023-04-16 21:35:20 +02:00
eigenein ba28620be7 New: add AvatarInfo 2022-12-07 15:40:34 +01:00
eigenein 3913046be7 Fix: forgotten AccountId leftover 2022-12-07 15:22:11 +01:00
eigenein de6deb3a06 Break: extract the re-imports to prelude 2022-12-07 15:21:21 +01:00
eigenein e1d9c27bda Break: rename author_results into author and add author.account_id 2022-12-07 14:17:23 +01:00
eigenein d2fb61425a New: initial author_results 2022-12-06 19:45:01 +01:00
eigenein 93cea446f5 New: add winning_team 2022-12-06 19:08:56 +01:00
eigenein 8d45bb28ed Opt: add .gitattributes 2022-12-06 01:14:01 +01:00
eigenein 1e0ce05c18 New: derive Serialize on battle results 2022-12-05 22:26:47 +01:00
eigenein f63ba599ec Fix: improved the docstring 2022-12-05 20:11:00 +01:00
eigenein f619ffd93e New: add LICENSE 2022-12-05 19:47:14 +01:00
Pavel Perestoronin 7d99456aaa Opt: add the docstrings, make the BattleResultsDat's fields public 2022-12-05 16:03:26 +01:00
Pavel Perestoronin be4a6c21b5 New: extract Replay::read_battle_results_dat 2022-12-05 15:50:13 +01:00
Pavel Perestoronin 805a2af81b Opt: add the keywords and categories 2022-12-03 21:15:15 +01:00
Pavel Perestoronin 899456eaf7 Break: distinguish the victory points seized vs earned 2022-12-03 21:05:52 +01:00
Pavel Perestoronin 96e63f81f4 Opt: move the test to README.md 2022-12-03 20:52:51 +01:00
eigenein 5786253cd5 New: set up sponsorship 2022-12-03 20:36:28 +01:00
Pavel Perestoronin e1f38014b8 New: add clan_tag 2022-12-03 20:34:44 +01:00
Pavel Perestoronin 7376cfc97e New: add n_enemies_damaged, n_enemies_destroyed, and credits 2022-12-03 20:28:18 +01:00
Pavel Perestoronin bc3b39e764 New: add tank_id 2022-12-03 20:11:49 +01:00
Pavel Perestoronin 4edcc50a87 Fix: don't clean up the pre-release flag 2022-12-03 20:06:35 +01:00
Pavel Perestoronin bccb5b519e New: reverse-engineer PlayerResultsInfo 2022-12-03 20:03:24 +01:00
Pavel Perestoronin ac086b1eb2 Break: refactor the parsers 2022-12-03 18:21:32 +01:00
Pavel Perestoronin 68e00c5eca Break: extract TeamNumber 2022-12-03 18:13:57 +01:00
Pavel Perestoronin 6fb70da06f Opt: extract BattleResultsDat::from_reader 2022-12-03 17:59:04 +01:00
Pavel Perestoronin 679b0ddadf Opt: avoid the buffer allocation, use a reader 2022-12-03 17:49:47 +01:00
Pavel Perestoronin 21206737c0 Break: refactor the models 2022-12-03 17:48:42 +01:00
22 changed files with 562 additions and 75 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'
+6
View File
@@ -49,3 +49,9 @@ jobs:
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --all-targets --all-features --tests -- -D warnings
- name: Publish the crates (dry run)
run: |
cargo publish --allow-dirty --dry-run
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
-2
View File
@@ -42,11 +42,9 @@ jobs:
with:
release_name: ${{ github.ref_name }}
body: ${{ steps.changelog.outputs.changelog }}
prerelease: false
- name: Publish the crates
run: |
cargo login
cargo publish --allow-dirty
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
+4 -2
View File
@@ -7,16 +7,18 @@ 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 = []
[dependencies]
prost = "0.11.3"
serde = { version = "1.0.148", features = ["derive"] }
serde_bytes = "0.11.7"
serde-pickle = "1.1.1"
serde_json = "1.0.96"
thiserror = "1.0.37"
zip = { version = "0.6.3", default-features = false, features = ["deflate"] }
+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 -1
View File
@@ -4,10 +4,38 @@ World of Tanks Blitz replay parser in Rust.
[![Crates.io](https://img.shields.io/crates/v/wotbreplay-parser)](https://crates.io/crates/wotbreplay-parser)
[![Last commit](https://img.shields.io/github/last-commit/eigenein/wotbreplay-parser)](https://github.com/eigenein/wotbreplay-parser/commits/main)
[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/eigenein/wotbreplay-parser/Check)](https://github.com/eigenein/wotbreplay-parser/actions)
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/eigenein/wotbreplay-parser/check.yaml)](https://github.com/eigenein/wotbreplay-parser/actions)
![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::prelude::*;
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(), 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(), TeamNumber::One);
assert_eq!(battle_results.players[1].info.platoon_id, Some(547466834));
Ok(())
}
```
## Replay structure
`*.wotbreplay` is a ZIP-archive containing:
+7 -7
View File
@@ -2,18 +2,18 @@ use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("failed to open the ZIP-archive")]
#[error("failed to open the replay as a ZIP-archive")]
OpenArchiveFailed(#[source] zip::result::ZipError),
#[error("failed to open the battle results entry in the replay archive")]
OpenBattleResultsFailed(#[source] zip::result::ZipError),
#[error("failed to open the entry in the replay archive")]
OpenEntryFailed(#[source] zip::result::ZipError),
#[error("failed to read the archive entry")]
ReadEntryFailed(#[source] std::io::Error),
#[error("failed to decode")]
#[error("failed to decode the Protocol Buffers encoded data")]
DecodeFailed(#[source] prost::DecodeError),
#[error("failed to un-pickle")]
UnpickleFailed(#[source] serde_pickle::Error),
#[error("failed to decode JSON")]
JsonDecodeFailed(#[source] serde_json::Error),
}
+29 -4
View File
@@ -1,8 +1,33 @@
//! World of Tanks Blitz replay parser.
#![warn(
clippy::all,
clippy::explicit_into_iter_loop,
clippy::manual_let_else,
clippy::map_unwrap_or,
clippy::missing_const_for_fn,
clippy::needless_pass_by_value,
clippy::trivially_copy_pass_by_ref,
clippy::unused_self
)]
mod error;
pub mod models;
mod models;
pub mod prelude;
mod replay;
mod result;
pub use crate::error::Error;
pub use crate::replay::Replay;
pub use crate::result::Result;
pub use self::error::Error;
pub use self::result::Result;
#[cfg(doctest)]
mod test_readme {
macro_rules! external_doc_test {
($x:expr) => {
#[doc = $x]
extern "C" {}
};
}
external_doc_test!(include_str!("../README.md"));
}
+6 -30
View File
@@ -1,31 +1,7 @@
use ::prost::Message;
mod battle_results;
mod battle_results_dat;
mod meta;
#[derive(Message)]
pub struct BattleResults {
#[prost(int64, tag = "2")]
pub timestamp: i64,
#[prost(message, repeated, tag = "201")]
pub players: Vec<Player>,
}
#[derive(Message)]
pub struct Player {
#[prost(uint32, tag = "1")]
pub account_id: u32,
#[prost(message, required, tag = "2")]
pub info: PlayerInfo,
}
#[derive(Message)]
pub struct PlayerInfo {
#[prost(string, tag = "1")]
pub nickname: String,
#[prost(uint32, optional, tag = "2")]
pub platoon_id: Option<u32>,
#[prost(uint32, tag = "3")]
pub team_number: u32,
}
pub use self::battle_results::*;
pub use self::battle_results_dat::*;
pub use self::meta::*;
+219
View File
@@ -0,0 +1,219 @@
use prost::bytes::Buf;
use prost::Message;
use serde::Serialize;
use crate::error::Error;
use crate::result::Result;
impl BattleResults {
/// Parse battle results from the buffer.
///
/// Note, that this does NOT parse `battle_results.dat` itself, but the un-pickled tuple element.
pub fn from_buffer(buffer: impl Buf) -> Result<Self> {
Self::decode(buffer).map_err(Error::DecodeFailed)
}
}
/// Parsed battle results.
///
/// TODO: 150[0].21[0].8[0].801[0].unsigned is arena end time.
#[derive(Message, Serialize)]
pub struct BattleResults {
/// Battle timestamp.
#[prost(int64, tag = "2")]
pub timestamp: i64,
#[prost(enumeration = "TeamNumber", tag = "3")]
pub winning_team: i32,
/// Replay's author results.
#[prost(message, required, tag = "8")]
pub author: Author,
/// Author's free XP, including premium.
#[prost(uint32, tag = "137")]
pub free_xp: u32,
/// Players in the battle.
#[prost(message, repeated, tag = "201")]
pub players: Vec<Player>,
/// Player's results.
#[prost(message, repeated, tag = "301")]
pub player_results: Vec<PlayerResults>,
}
/// Battle player.
#[derive(Message, Serialize)]
pub struct Player {
/// Player's account ID.
#[prost(uint32, tag = "1")]
pub account_id: u32,
/// Player's extended information.
#[prost(message, required, tag = "2")]
pub info: PlayerInfo,
}
/// Player's extended information.
#[derive(Message, Serialize)]
pub struct PlayerInfo {
/// Player's nickname.
#[prost(string, tag = "1")]
pub nickname: String,
/// Some sort of platoon ID:
/// - contains same ID for a platoon members, or
/// - `None` for non-platoon players
#[prost(uint32, optional, tag = "2")]
pub platoon_id: Option<u32>,
/// Player's team assignment.
#[prost(enumeration = "TeamNumber", tag = "3")]
pub team: i32,
#[prost(string, optional, tag = "5")]
pub clan_tag: Option<String>,
#[prost(message, required, tag = "7")]
pub avatar: Avatar,
}
#[derive(
Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, prost::Enumeration, Serialize,
)]
#[repr(i32)]
pub enum TeamNumber {
One = 1,
Two = 2,
}
/// Player's battle results.
#[derive(Message, Serialize)]
pub struct PlayerResults {
/// Looks like some sort of sequential result ID. They reference each other,
/// but I haven't figured this out yet.
#[prost(uint32, tag = "1")]
pub result_id: u32,
/// Extended player's results information.
#[prost(message, required, tag = "2")]
pub info: PlayerResultsInfo,
}
/// Extended player's results information.
#[derive(Message, Serialize)]
pub struct PlayerResultsInfo {
/// Credits earned – without special awards and medals and premium account excluded.
#[prost(uint32, tag = "2")]
pub credits_earned: u32,
/// Base XP (the total without multipliers).
#[prost(uint32, tag = "3")]
pub base_xp: u32,
#[prost(uint32, tag = "4")]
pub n_shots: u32,
#[prost(uint32, tag = "5")]
pub n_hits_dealt: u32,
#[prost(uint32, tag = "7")]
pub n_penetrations_dealt: u32,
#[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,
#[prost(uint32, tag = "12")]
pub n_hits_received: u32,
#[prost(uint32, tag = "13")]
pub n_non_penetrating_hits_received: u32,
#[prost(uint32, tag = "15")]
pub n_penetrations_received: u32,
#[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_earned: u32,
#[prost(uint32, tag = "33")]
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.
/// This field matches the `mm_rating` as returned by the Wargaming.net API.
#[prost(float, optional, tag = "107")]
pub mm_rating: Option<f32>,
#[prost(uint32, tag = "117")]
pub damage_blocked: u32,
}
impl PlayerResultsInfo {
/// Returns displayed rating as per the game client.
pub fn display_rating(&self) -> Option<u32> {
self.mm_rating
.map(|mm_rating| (mm_rating * 10.0 + 3000.0) as u32)
}
}
#[derive(Message, Serialize)]
pub struct Author {
/// Earned credits, including premium and special awards and medals.
#[prost(uint32, tag = "2")]
pub credits_earned: u32,
/// Combat XP, including premium.
#[prost(uint32, tag = "3")]
pub combat_xp: u32,
#[prost(uint32, tag = "8")]
pub damage_dealt: u32,
#[prost(uint32, tag = "101")]
pub account_id: u32,
#[prost(enumeration = "TeamNumber", tag = "102")]
pub team_number: i32,
}
#[derive(Message, Serialize)]
pub struct Avatar {
#[prost(message, required, tag = "2")]
pub info: AvatarInfo,
}
#[derive(Message, Serialize)]
pub struct AvatarInfo {
#[prost(string, tag = "2")]
pub gfx_url: String,
#[prost(string, tag = "3")]
pub gfx2_url: String,
#[prost(string, tag = "4")]
pub kind: String,
}
+47
View File
@@ -0,0 +1,47 @@
use std::io::Read;
use prost::Message;
use serde::Deserialize;
use serde_bytes::ByteBuf;
use crate::error::Error;
use crate::models::BattleResults;
use crate::result::Result;
/// Represents un-pickled `battle_results.dat`.
#[derive(Debug, Deserialize)]
#[serde(from = "(u64, ByteBuf)")]
pub struct BattleResultsDat {
pub arena_unique_id: u64,
/// Protobuf-serialized battle results.
pub buffer: ByteBuf,
}
impl From<(u64, ByteBuf)> for BattleResultsDat {
fn from((arena_unique_id, buffer): (u64, ByteBuf)) -> Self {
Self {
arena_unique_id,
buffer,
}
}
}
impl BattleResultsDat {
/// Parses the pickled structure from the reader which contains `battle_results.dat`.
///
/// It can be used either on the `battle_results.dat` for a replay archive,
/// or directly on `**/DAVAProject/battle_results/*/*_full.dat`.
pub fn from_reader(reader: impl Read) -> Result<Self> {
serde_pickle::from_reader(reader, Default::default()).map_err(Error::UnpickleFailed)
}
}
impl TryInto<BattleResults> for BattleResultsDat {
type Error = Error;
/// Decodes the battle results from the internal buffer.
fn try_into(self) -> Result<BattleResults> {
BattleResults::decode(self.buffer.as_ref()).map_err(Error::DecodeFailed)
}
}
+28
View File
@@ -0,0 +1,28 @@
use std::io::Read;
use serde::Deserialize;
use crate::error::Error;
use crate::result::Result;
/// The model for `meta.json`.
#[derive(Deserialize)]
pub struct Meta {
#[serde(rename = "playerName")]
pub player_name: String,
#[serde(rename = "arenaUniqueId")]
pub arena_unique_id: String,
#[serde(rename = "battleDuration")]
pub battle_duration_secs: f64,
#[serde(rename = "vehicleCompDescriptor")]
pub tank_id: u16,
}
impl Meta {
pub fn from_reader(reader: impl Read) -> Result<Self> {
serde_json::from_reader(reader).map_err(Error::JsonDecodeFailed)
}
}
+2
View File
@@ -0,0 +1,2 @@
pub use crate::models::*;
pub use crate::replay::Replay;
+20 -14
View File
@@ -1,35 +1,41 @@
use std::io::{Read, Seek};
use prost::Message;
use serde_bytes::ByteBuf;
use zip::ZipArchive;
use crate::error::Error;
use crate::models::BattleResults;
use crate::models::{BattleResults, BattleResultsDat, Meta};
use crate::result::Result;
/// Opened (but not yet parsed) replay.
pub struct Replay<R>(pub(crate) ZipArchive<R>);
impl<R: Read + Seek> Replay<R> {
/// Opens a replay from the reader.
/// Opens a replay from the reader which contains a `*.wotbreplay`.
pub fn open(reader: R) -> Result<Self> {
ZipArchive::new(reader)
.map_err(Error::OpenArchiveFailed)
.map(Self)
}
/// Reads and parses the battle results from the replay.
pub fn read_battle_results(&mut self) -> Result<BattleResults> {
let mut pickled_battle_results = self
self.read_battle_results_dat()?.try_into()
}
/// 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)?;
let mut serialized_battle_results = Vec::new();
pickled_battle_results
.read_to_end(&mut serialized_battle_results)
.map_err(Error::ReadEntryFailed)?;
let (_, battle_results): (u64, ByteBuf) =
serde_pickle::from_slice(&serialized_battle_results, Default::default())
.map_err(Error::UnpickleFailed)?;
BattleResults::decode(battle_results.as_ref()).map_err(Error::DecodeFailed)
.map_err(Error::OpenEntryFailed)?;
BattleResultsDat::from_reader(pickled_battle_results)
}
pub fn read_meta(&mut self) -> Result<Meta> {
Meta::from_reader(
self.0
.by_name("meta.json")
.map_err(Error::OpenEntryFailed)?,
)
}
}
+122 -15
View File
@@ -1,26 +1,133 @@
use std::fs::File;
use anyhow::Result;
use wotbreplay_parser::Replay;
use wotbreplay_parser::prelude::*;
#[test]
fn battle_results_ok() -> Result<()> {
let mut replay = Replay::open(File::open(
"tests/replays/20221202_2259__zeekrab_It20_Car_Comb_45t_2308705958773814583.wotbreplay",
)?)?;
let battle_results = replay.read_battle_results()?;
fn player_results_ok() -> Result<()> {
let battle_results_dat = Replay::open(File::open("tests/replays/player_results.wotbreplay")?)?
.read_battle_results_dat()?;
assert_eq!(battle_results_dat.arena_unique_id, 1661909200500084);
let battle_results: BattleResults = battle_results_dat.try_into()?;
assert_eq!(battle_results.timestamp, 1670018359);
assert_eq!(battle_results.players.len(), 14);
let snak_the_ripper_info = &battle_results.player_results[3].info;
assert_eq!(snak_the_ripper_info.base_xp, 929);
assert_eq!(snak_the_ripper_info.damage_dealt, 2584);
assert_eq!(snak_the_ripper_info.damage_assisted_1, 37);
assert_eq!(snak_the_ripper_info.account_id, 566225799);
assert_eq!(snak_the_ripper_info.display_rating(), Some(4070));
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);
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, 2);
assert_eq!(battle_results.players[0].info.platoon_id, None);
let zeekrab_info = &battle_results.player_results[10].info;
assert_eq!(zeekrab_info.base_xp, 701);
assert_eq!(zeekrab_info.damage_dealt, 1438);
assert_eq!(zeekrab_info.damage_assisted_2, 31);
assert_eq!(zeekrab_info.damage_blocked, 190);
assert_eq!(zeekrab_info.account_id, 594778041);
assert_eq!(zeekrab_info.display_rating(), Some(4277));
assert_eq!(zeekrab_info.n_shots, 5);
assert_eq!(zeekrab_info.n_hits_dealt, 5);
assert_eq!(zeekrab_info.n_penetrations_dealt, 5);
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_earned, 40);
assert_eq!(zeekrab_info.victory_points_seized, 40);
assert_eq!(zeekrab_info.tank_id, 26657);
assert_eq!(zeekrab_info.credits_earned, 37679);
assert_eq!(zeekrab_info.n_enemies_damaged, 2);
assert_eq!(zeekrab_info.n_enemies_destroyed, 1);
assert_eq!(battle_results.players[1].info.nickname, "SNAK_THE_RIPPER");
assert_eq!(battle_results.players[1].info.team_number, 1);
assert_eq!(battle_results.players[1].info.platoon_id, Some(547466834));
let balls_soup_info = &battle_results.player_results[8].info;
assert_eq!(balls_soup_info.damage_dealt, 2064);
assert_eq!(balls_soup_info.display_rating(), None);
assert_eq!(balls_soup_info.n_shots, 9);
assert_eq!(balls_soup_info.n_hits_dealt, 8);
assert_eq!(balls_soup_info.n_penetrations_dealt, 8);
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);
assert_eq!(ceaser_info.display_rating(), Some(4439));
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 player_info_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[10].info.avatar.info.kind, "legendary");
assert_eq!(
battle_results.players[10].info.avatar.info.gfx_url,
"https://stufficons.wgcdn.co/Gfx/avatar_UFO_legendary_anim/220523_104739_752791.webp"
);
assert_eq!(
battle_results.players[10].info.avatar.info.gfx2_url,
"https://stufficons.wgcdn.co/Gfx2/avatar_UFO_legendary_anim/220523_104755_251331.webp"
);
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(())
}
#[test]
fn winning_team_ok() -> Result<()> {
let battle_results = Replay::open(File::open("tests/replays/winning_team.wotbreplay")?)?
.read_battle_results()?;
assert_eq!(battle_results.winning_team(), TeamNumber::One);
Ok(())
}
#[test]
fn author_results_ok() -> Result<()> {
let battle_results = Replay::open(File::open("tests/replays/author_results.wotbreplay")?)?
.read_battle_results()?;
assert_eq!(battle_results.author.credits_earned, 53623);
assert_eq!(battle_results.author.combat_xp, 2370);
assert_eq!(battle_results.author.damage_dealt, 1738);
assert_eq!(battle_results.author.account_id, 594778041);
assert_eq!(battle_results.free_xp, 116);
Ok(())
}
+16
View File
@@ -0,0 +1,16 @@
use std::fs::File;
use anyhow::Result;
use wotbreplay_parser::prelude::*;
#[test]
fn meta_ok() -> Result<()> {
let meta = Replay::open(File::open("tests/replays/player_results.wotbreplay")?)?.read_meta()?;
assert_eq!(meta.player_name, "zeekrab");
assert_eq!(meta.arena_unique_id, "1661909200500084");
assert_eq!(meta.battle_duration_secs, 164.08485);
assert_eq!(meta.tank_id, 26657);
Ok(())
}
+1
View File
@@ -0,0 +1 @@
*.wotbreplay linguist-generated
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.