Skip to content

Commit

Permalink
💥 Improve the docs and error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
eigenein committed May 8, 2023
1 parent 838a49e commit c986abc
Show file tree
Hide file tree
Showing 10 changed files with 59 additions and 20 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use wotbreplay_parser::replay::Replay;
fn main() -> Result<()> {
let battle_results = Replay::open(File::open("replays/20221203_player_results.wotbreplay")?)?.read_battle_results()?;

assert_eq!(battle_results.timestamp, 1670083956);
assert_eq!(battle_results.timestamp_secs, 1670083956);
assert_eq!(battle_results.players.len(), 14);

assert_eq!(battle_results.players[0].account_id, 595693744);
Expand Down
14 changes: 9 additions & 5 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,13 @@ pub enum Error {
#[error("JSON error")]
JsonError(#[from] serde_json::Error),

#[error("invalid magic: {0:#X}, expected: {1:#X}")]
InvalidMagic(u32, u32),

#[error("failed to read a packet length, got {0} bytes")]
PacketLengthError(usize),
#[error("invalid magic: {actual:#X}, expected: {expected:#X}")]
InvalidMagic { expected: u32, actual: u32 },

#[error("failed to parse type {type_} packet's payload at replay time {clock_secs}s")]
PacketPayloadParsingError {
source: Box<Error>,
type_: u32,
clock_secs: f32,
},
}
15 changes: 9 additions & 6 deletions src/models/battle_results.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ impl BattleResults {
///
/// # Note
///
/// This does **not** parse `battle_results.dat` itself, but the un-pickled tuple element.
/// This can **not** parse `battle_results.dat` itself, but the un-pickled tuple element.
/// To parse `battle_results.dat`, use [`crate::models::battle_results_dat::BattleResultsDat`].
pub fn from_buffer(buffer: impl Buf) -> Result<Self> {
Ok(Self::decode(buffer)?)
Expand All @@ -23,7 +23,7 @@ impl BattleResults {
pub struct BattleResults {
/// Battle timestamp.
#[prost(int64, tag = "2")]
pub timestamp: i64,
pub timestamp_secs: i64,

#[prost(enumeration = "TeamNumber", optional, tag = "3")]
pub winner_team_number: Option<i32>,
Expand All @@ -40,7 +40,7 @@ pub struct BattleResults {
#[prost(message, repeated, tag = "201")]
pub players: Vec<Player>,

/// Player's results.
/// Individual player's results.
#[prost(message, repeated, tag = "301")]
pub player_results: Vec<PlayerResults>,
}
Expand All @@ -65,8 +65,9 @@ pub struct PlayerInfo {
pub nickname: String,

/// Some sort of platoon ID:
/// - contains same ID for a platoon members, or
/// - `None` for non-platoon players
///
/// - contains same ID for the platoon members, or
/// - [`None`] for non-platoon players
#[prost(uint32, optional, tag = "2")]
pub platoon_id: Option<u32>,

Expand Down Expand Up @@ -165,6 +166,8 @@ pub struct PlayerResultsInfo {
///
/// Note, that this is **not** the game client's displayed rating.
/// This field matches the `mm_rating` as returned by the Wargaming.net API.
///
/// The display rating is calculated as: `3000.0 + mm_rating * 10.0`.
#[prost(float, optional, tag = "107")]
pub mm_rating: Option<f32>,

Expand All @@ -173,7 +176,7 @@ pub struct PlayerResultsInfo {
}

impl PlayerResultsInfo {
/// Returns displayed rating as per the game client.
/// Calculate displayed rating according to the game client.
pub fn display_rating(&self) -> Option<u32> {
self.mm_rating
.map(|mm_rating| (mm_rating * 10.0 + 3000.0) as u32)
Expand Down
11 changes: 9 additions & 2 deletions src/models/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ fn assert_magic<T: Into<u32> + PartialEq>(actual: T, expected: T) -> Result {
if actual == expected {
Ok(())
} else {
Err(Error::InvalidMagic(actual.into(), expected.into()))
Err(Error::InvalidMagic {
actual: actual.into(),
expected: expected.into(),
})
}
}

Expand Down Expand Up @@ -82,7 +85,11 @@ fn read_pickled<T: DeserializeOwned>(reader: &mut impl Read, length: usize) -> R
/// Read 2-byte length surrounded by the magical `0xFF` and `0x00`,
/// unless it's a simple 1-byte length.
///
/// P.S. Okay, Wargaming, I've no idea what you smoke, but I want some!
/// # Context
///
/// It seems that for payloads larger than 254 bytes, Wargaming writes `00 <XX XX> FF`,
/// where the word in the middle is the actual length of a payload, and the `00` and `FF`
/// are magic values.
#[inline]
fn read_quirky_length(reader: &mut impl Read) -> Result<usize> {
match reader.read_u8()? {
Expand Down
14 changes: 13 additions & 1 deletion src/models/data/base_player_create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,16 @@ pub struct BasePlayerCreate {
#[serde(rename(deserialize = "camouflageSlot"))]
pub camouflage_slot: u8,

/// Average team ratings for rating battles.
///
/// # Contains
///
/// - [`Some`] 2-vector, if the attribute is present in the replay. Each element represents one team:
/// - [`Some`] rating, if known
/// - [`None`], if the average rating is unknown (all the players are calibrating)
/// - [`None`], if the attribute is missing in the replay
#[serde(default, rename(deserialize = "avgMmr"))]
pub average_mmr: Option<Vec<Option<f64>>>,
pub average_mm_ratings: Option<Vec<Option<f64>>>,

#[serde(default, rename(deserialize = "playerWaitTimes"))]
pub player_wait_times: Option<HashMap<u32, f64>>,
Expand All @@ -35,6 +43,10 @@ pub struct BasePlayerCreate {
pub turbo_battles_statistics: Option<TurboBattlesStatistics>,
}

/// I don't know what that is:
///
/// - On EU, Asia, and NA the values are just constants
/// - On Russia, the `alpha` and `beta` are different constants, and the chances actually differ for different replays
#[derive(Debug, Serialize, Deserialize)]
pub struct TurboBattlesStatistics {
#[serde(rename(deserialize = "turboBattleChance"))]
Expand Down
2 changes: 1 addition & 1 deletion src/models/data/entity_method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub enum EntityMethod {
}

impl EntityMethod {
/// Parse the entity method payload.
/// Parse the entity method raw payload.
pub fn new(raw_payload: &[u8]) -> Result<Self> {
let mut raw_payload = raw_payload;

Expand Down
15 changes: 14 additions & 1 deletion src/models/data/packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use serde_with::serde_as;

use crate::models::data::payload::Payload;
use crate::result::Result;
use crate::Error;

/// «Packet» is a single traffic message in a replay,
/// packets tell us what is going on during the battle.
Expand All @@ -28,14 +29,26 @@ pub struct Packet {
}

impl Packet {
/// Parse next packet from the reader.
///
/// # Returns
///
/// Parsed packet, or [`None`], when no packets are left.
pub fn from_reader(reader: &mut impl Read) -> Result<Option<Self>> {
let Ok(length) = reader.read_u32::<LittleEndian>() else { return Ok(None) };
let type_ = reader.read_u32::<LittleEndian>()?;
let clock_secs = reader.read_f32::<LittleEndian>()?;
let raw_payload = Self::read_raw_payload(reader, length as usize)?;
let payload = Payload::new(type_, &raw_payload).map_err(|source| {
Error::PacketPayloadParsingError {
source: Box::new(source),
type_,
clock_secs,
}
})?;
let this = Self {
clock_secs,
payload: Payload::new(type_, &raw_payload)?,
payload,

#[cfg(feature = "raw-payload")]
raw_payload,
Expand Down
2 changes: 1 addition & 1 deletion src/models/data/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub enum Payload {
}

impl Payload {
/// Parse the packet payload.
/// Parse the raw packet payload.
pub fn new(packet_type: u32, raw_payload: &[u8]) -> Result<Self> {
let mut raw_payload = raw_payload;

Expand Down
2 changes: 1 addition & 1 deletion src/replay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use crate::result::Result;
pub struct Replay<R>(pub(crate) ZipArchive<R>);

impl<R: Read + Seek> Replay<R> {
/// Opens a replay from the reader which contains a `*.wotbreplay`.
/// Open a replay from the reader which contains a `*.wotbreplay`.
pub fn open(reader: R) -> Result<Self> {
Ok(Self(ZipArchive::new(reader)?))
}
Expand Down
2 changes: 1 addition & 1 deletion tests/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ fn base_player_create_ok() -> Result<()> {
assert_eq!(arguments.matchmaker_type, 2);
assert_eq!(arguments.camouflage_slot, 1);
assert_eq!(
arguments.average_mmr,
arguments.average_mm_ratings,
Some(vec![Some(59.29457753400008), Some(99.34182121604681)])
);
assert_eq!(
Expand Down

0 comments on commit c986abc

Please sign in to comment.