diff --git a/src/constants/mod.rs b/src/constants/mod.rs index c13024c..d9e609f 100644 --- a/src/constants/mod.rs +++ b/src/constants/mod.rs @@ -1167,3 +1167,30 @@ pub enum ReadyStatus { Ready = 1, Spectating = 2, } + +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + Ord, + PartialOrd, + Hash, + Serialize, + Deserialize, +)] +pub struct LapValid(u8); + +bitflags! { + impl LapValid: u8 { + /// Whether the whole lap is valid. Has a value of `0b0001` + const Overall = 0b0001; + /// Whether the sector 1 run is valid. Has a value of `0b0010` + const Sector1 = 0b0010; + /// Whether the sector 2 run is valid. Has a value of `0b0100` + const Sector2 = 0b0100; + /// Whether the sector 3 run is valid. Has a value of `0b1000` + const Sector3 = 0b1000; + } +} diff --git a/src/lib.rs b/src/lib.rs index f53ad7d..a0dd8bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ use crate::packets::{ F1PacketCarDamage, F1PacketCarSetups, F1PacketCarStatus, F1PacketCarTelemetry, F1PacketEvent, F1PacketFinalClassification, F1PacketLap, F1PacketLobbyInfo, F1PacketMotion, F1PacketParticipants, - F1PacketSession, + F1PacketSession, F1PacketSessionHistory, }; use binrw::io::Cursor; @@ -107,4 +107,7 @@ pub struct F1PacketBody { /// Car damage parameters for all cars in the session. #[br(if(packet_id == PacketId::CarDamage), args(packet_format))] pub car_damage: Option, + /// Session history data for a specific car. + #[br(if(packet_id == PacketId::SessionHistory), args(packet_format))] + pub session_history: Option, } diff --git a/src/packets/mod.rs b/src/packets/mod.rs index 5fffff4..bdcbd7e 100644 --- a/src/packets/mod.rs +++ b/src/packets/mod.rs @@ -1,20 +1,22 @@ -mod car_damage; +pub mod car_damage; pub mod car_setups; pub mod car_status; pub mod car_telemetry; pub mod event; pub mod final_classification; pub mod lap; -mod lobby; +pub mod lobby; pub mod motion; pub mod participants; pub mod session; +pub mod session_history; use crate::constants::{ BrakingAssist, DynamicRacingLine, DynamicRacingLineType, ForecastAccuracy, Formula, GameMode, GearboxAssist, MfdPanelIndex, Ruleset, SafetyCarStatus, SessionLength, SessionType, TrackId, Weather, MAX_NUM_CARS, }; +use crate::packets::car_damage::CarDamageData; use crate::packets::car_setups::CarSetupData; use crate::packets::car_status::CarStatusData; use crate::packets::car_telemetry::CarTelemetryData; @@ -25,8 +27,8 @@ use crate::packets::lobby::LobbyInfoData; use crate::packets::motion::CarMotionData; use crate::packets::participants::ParticipantsData; use crate::packets::session::{MarshalZone, WeatherForecastSample}; +use crate::packets::session_history::{LapHistoryData, TyreStintHistoryData}; -use crate::packets::car_damage::CarDamageData; use binrw::BinRead; use serde::{Deserialize, Serialize}; use std::string::FromUtf8Error; @@ -353,6 +355,60 @@ pub struct F1PacketCarDamage { pub car_damage_data: Vec, } +/// Packet detailing lap and tyre data history for a given driver in the session +#[non_exhaustive] +#[derive( + BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize, +)] +#[br( + little, + import(packet_format: u16), + assert( + vehicle_index < MAX_NUM_CARS, + "Session history packet has an invalid vehicle index: {}", + vehicle_index + ), + assert( + num_laps <= 100, + "Session history packet has an invalid number of laps: {}", + num_laps + ), + assert( + num_tyre_stints <= 8, + "Session history packet has an invalid number of tyre stints: {}", + num_tyre_stints + ) +)] +pub struct F1PacketSessionHistory { + /// Index of the car this packet refers to + #[br(map(u8_to_usize))] + pub vehicle_index: usize, + /// Number of laps in the data (including the current one) + #[br(map(u8_to_usize))] + pub num_laps: usize, + /// Number of tyre stints in the data (including the current one) + #[br(map(u8_to_usize))] + pub num_tyre_stints: usize, + /// Number of the lap the best lap time was achieved on + #[br(map(u8_to_usize))] + pub best_lap_time_lap_num: usize, + /// Number of the lap the best sector 1 time was achieved on + #[br(map(u8_to_usize))] + pub best_sector1_lap_num: usize, + /// Number of the lap the best sector 2 time was achieved on + #[br(map(u8_to_usize))] + pub best_sector2_lap_num: usize, + /// Number of the lap the best sector 3 time was achieved on + #[br(map(u8_to_usize))] + pub best_sector3_lap_num: usize, + /// Up to 100 laps + #[br(count(100), args{ inner: (packet_format,) })] + pub lap_history_data: Vec, + /// Up to 8 tyre stints + #[br(count(8), args{ inner: (packet_format,) })] + pub tyre_stint_history_data: Vec, +} + /// Extended motion data for player's car. Available as a: /// - part of [`F1PacketMotion`] in the 2022 format /// - standalone packet from the 2023 format onwards diff --git a/src/packets/session_history.rs b/src/packets/session_history.rs new file mode 100644 index 0000000..b61fcbd --- /dev/null +++ b/src/packets/session_history.rs @@ -0,0 +1,37 @@ +use super::u8_to_usize; +use crate::constants::{ActualTyreCompound, LapValid, VisualTyreCompound}; + +use binrw::BinRead; +use serde::{Deserialize, Serialize}; + +#[derive( + BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize, +)] +#[br(little, import(_packet_format: u16))] +pub struct LapHistoryData { + /// Lap time in milliseconds. + pub lap_time_ms: u32, + /// Sector 1 time in milliseconds. + pub sector1_time_ms: u16, + /// Sector 2 time in milliseconds. + pub sector2_time_ms: u16, + /// Sector 3 time in milliseconds. + pub sector3_time_ms: u16, + /// Bitmap of lap validity across all sectors and overall. + #[br(map(LapValid::from_bits_truncate))] + pub lap_valid_bit_flags: LapValid, +} + +#[derive( + BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize, +)] +#[br(little, import(_packet_format: u16))] +pub struct TyreStintHistoryData { + /// Lap the tyre usage ends on (255 if current tyre) + #[br(map(u8_to_usize))] + pub end_lap: usize, + /// Actual tyre compound used + pub actual_tyre_compound: ActualTyreCompound, + /// Visual tyre compound used + pub visual_tyre_compound: VisualTyreCompound, +}