feat: add 2022 spec final classification packet parser

This commit is contained in:
Maciej Pędzich 2025-02-19 11:22:54 +01:00
parent 2ccfdae321
commit 32471a952d
Signed by: maciejpedzich
GPG Key ID: CE4A303D84882F0D
4 changed files with 124 additions and 38 deletions

View File

@ -3,9 +3,9 @@ pub mod packets;
use crate::constants::PacketId; use crate::constants::PacketId;
use crate::packets::{ use crate::packets::{
F1PacketCarSetupsData, F1PacketCarStatusData, F1PacketCarTelemetryData, F1PacketCarSetups, F1PacketCarStatus, F1PacketCarTelemetry, F1PacketEvent,
F1PacketEventData, F1PacketLapData, F1PacketMotionData, F1PacketFinalClassification, F1PacketLap, F1PacketMotion,
F1PacketParticipantsData, F1PacketSessionData, F1PacketParticipants, F1PacketSession,
}; };
use binrw::io::Cursor; use binrw::io::Cursor;
@ -73,26 +73,31 @@ pub struct F1PacketHeader {
)] )]
#[br(little, import(packet_format: u16, packet_id: PacketId))] #[br(little, import(packet_format: u16, packet_id: PacketId))]
pub struct F1PacketBody { pub struct F1PacketBody {
/// Physics data for all the cars being driven. /// Physics data for all cars in the session.
#[br(if(packet_id == PacketId::Motion), args(packet_format))] #[br(if(packet_id == PacketId::Motion), args(packet_format))]
pub motion: Option<F1PacketMotionData>, pub motion: Option<F1PacketMotion>,
/// Data about the ongoing session. /// Data about the ongoing session.
#[br(if(packet_id == PacketId::Session), args(packet_format))] #[br(if(packet_id == PacketId::Session), args(packet_format))]
pub session: Option<F1PacketSessionData>, pub session: Option<F1PacketSession>,
/// Lap data for all cars on track. /// Lap data for all cars on track.
#[br(if(packet_id == PacketId::LapData), args(packet_format))] #[br(if(packet_id == PacketId::LapData), args(packet_format))]
pub lap: Option<F1PacketLapData>, pub lap: Option<F1PacketLap>,
/// Details of events that happen during the course of a session. /// Details of events that happen during the course of a session.
#[br(if(packet_id == PacketId::Event), args(packet_format))] #[br(if(packet_id == PacketId::Event), args(packet_format))]
pub event: Option<F1PacketEventData>, pub event: Option<F1PacketEvent>,
/// List of participants in the race. /// List of participants in the race.
#[br(if(packet_id == PacketId::Participants), args(packet_format))] #[br(if(packet_id == PacketId::Participants), args(packet_format))]
pub participants: Option<F1PacketParticipantsData>, pub participants: Option<F1PacketParticipants>,
/// Car setups for each vehicle in the session. /// Car setups for all cars in the session.
#[br(if(packet_id == PacketId::CarSetups), args(packet_format))] #[br(if(packet_id == PacketId::CarSetups), args(packet_format))]
pub car_setups: Option<F1PacketCarSetupsData>, pub car_setups: Option<F1PacketCarSetups>,
/// Telemetry data for all cars in the session.
#[br(if(packet_id == PacketId::CarTelemetry), args(packet_format))] #[br(if(packet_id == PacketId::CarTelemetry), args(packet_format))]
pub car_telemetry: Option<F1PacketCarTelemetryData>, pub car_telemetry: Option<F1PacketCarTelemetry>,
/// Status data for all cars in the session.
#[br(if(packet_id == PacketId::CarStatus), args(packet_format))] #[br(if(packet_id == PacketId::CarStatus), args(packet_format))]
pub car_status: Option<F1PacketCarStatusData>, pub car_status: Option<F1PacketCarStatus>,
/// Final classification confirmation at the end of a race.
#[br(if(packet_id == PacketId::FinalClassification), args(packet_format))]
pub final_classification: Option<F1PacketFinalClassification>,
} }

View File

@ -0,0 +1,50 @@
use super::u8_to_usize;
use crate::constants::{ActualTyreCompound, ResultStatus, VisualTyreCompound};
use binrw::BinRead;
use serde::{Deserialize, Serialize};
#[non_exhaustive]
#[derive(
BinRead, PartialEq, PartialOrd, Copy, Clone, Debug, Serialize, Deserialize,
)]
#[br(
little,
import(_packet_format: u16),
assert(
num_tyre_stints <= 8,
"Final classification entry has an invalid number of tyre stints: {}",
num_tyre_stints
)
)]
pub struct FinalClassificationData {
/// Finishing position.
pub position: u8,
/// Number of laps completed.
pub num_laps: u8,
/// Grid position of the car.
pub grid_position: u8,
/// Number of points scored.
pub points: u8,
/// Number of pit stops made.
pub num_pit_stops: u8,
/// Result status.
pub result_status: ResultStatus,
/// Best lap time of the session in milliseconds.
pub best_lap_time_ms: u32,
/// Total race time in seconds (without penalties).
pub total_race_time: f64,
/// Total penalties accumulated in seconds.
pub penalties_time: u8,
/// Number of penalties applied to this driver.
pub num_penalties: u8,
/// Number of tyre stints (no greater than 8).
#[br(map(u8_to_usize))]
pub num_tyre_stints: usize,
/// Actual tyres used by the driver.
pub tyre_stints_actual: [ActualTyreCompound; 8],
/// Visual tyres used by the driver.
pub tyre_stints_visual: [VisualTyreCompound; 8],
/// The lap numbers the stints end on.
pub tyre_stints_end_laps: [u8; 8],
}

View File

@ -2,7 +2,8 @@ pub mod car_setups;
pub mod car_status; pub mod car_status;
pub mod car_telemetry; pub mod car_telemetry;
pub mod event; pub mod event;
pub mod lap_data; pub mod final_classification;
pub mod lap;
pub mod motion; pub mod motion;
pub mod participants; pub mod participants;
pub mod session; pub mod session;
@ -12,15 +13,16 @@ use crate::constants::{
Formula, GameMode, GearboxAssist, MfdPanelIndex, Ruleset, SafetyCarStatus, Formula, GameMode, GearboxAssist, MfdPanelIndex, Ruleset, SafetyCarStatus,
SessionLength, SessionType, TrackId, Weather, MAX_NUM_CARS, SessionLength, SessionType, TrackId, Weather, MAX_NUM_CARS,
}; };
use crate::packets::car_setups::CarSetupData;
use crate::packets::car_status::CarStatusData;
use crate::packets::car_telemetry::CarTelemetryData;
use crate::packets::event::EventDataDetails; use crate::packets::event::EventDataDetails;
use crate::packets::lap_data::LapData; use crate::packets::lap::LapData;
use crate::packets::motion::CarMotionData; use crate::packets::motion::CarMotionData;
use crate::packets::participants::ParticipantsData; use crate::packets::participants::ParticipantsData;
use crate::packets::session::{MarshalZone, WeatherForecastSample}; use crate::packets::session::{MarshalZone, WeatherForecastSample};
use crate::packets::car_setups::CarSetupData; use crate::packets::final_classification::FinalClassificationData;
use crate::packets::car_status::CarStatusData;
use crate::packets::car_telemetry::CarTelemetryData;
use binrw::BinRead; use binrw::BinRead;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
@ -31,13 +33,13 @@ use std::string::FromUtf8Error;
BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize, BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize,
)] )]
#[br(little, import(packet_format: u16))] #[br(little, import(packet_format: u16))]
pub struct F1PacketMotionData { pub struct F1PacketMotion {
/// Motion data for all cars on track. /// Motion data for all cars on track.
#[br(count(MAX_NUM_CARS), args{ inner: (packet_format,) })] #[br(count(MAX_NUM_CARS), args{ inner: (packet_format,) })]
pub car_motion_data: Vec<CarMotionData>, pub car_motion_data: Vec<CarMotionData>,
/// Extra player car only motion data (2022 format only). /// Extra player car only motion data (2022 format only).
#[br(if(packet_format == 2022))] #[br(if(packet_format == 2022))]
pub motion_ex_data: Option<F1PacketMotionExData>, pub motion_ex_data: Option<F1PacketMotionEx>,
} }
/// Data about the ongoing session. /// Data about the ongoing session.
@ -65,7 +67,7 @@ pub struct F1PacketMotionData {
ai_difficulty ai_difficulty
), ),
)] )]
pub struct F1PacketSessionData { pub struct F1PacketSession {
/// Current weather. /// Current weather.
pub weather: Weather, pub weather: Weather,
/// Track temperature in degrees Celsius. /// Track temperature in degrees Celsius.
@ -104,7 +106,8 @@ pub struct F1PacketSessionData {
#[br(map(u8_to_usize))] #[br(map(u8_to_usize))]
pub num_marshal_zones: usize, pub num_marshal_zones: usize,
/// List of marshal zones. /// List of marshal zones.
/// Has a size of 21 regardless of the `num_marshal_zones` value. /// Has a size of 21 regardless of the
/// [`num_marshal_zones`](field@F1PacketSession::num_marshal_zones) value.
#[br(count(21), args{ inner: (packet_format,) })] #[br(count(21), args{ inner: (packet_format,) })]
pub marshal_zones: Vec<MarshalZone>, pub marshal_zones: Vec<MarshalZone>,
/// Safety car deployment status. /// Safety car deployment status.
@ -116,7 +119,9 @@ pub struct F1PacketSessionData {
#[br(map(u8_to_usize))] #[br(map(u8_to_usize))]
pub num_weather_forecast_samples: usize, pub num_weather_forecast_samples: usize,
/// List of up to weather forecast samples. /// List of up to weather forecast samples.
/// Has a size of 56 regardless of the `num_weather_forecast_samples` value. /// Has a size of 56 regardless of the
/// [`num_weather_forecast_samples`](field@F1PacketSession::num_weather_forecast_samples)
/// value.
#[br(count(56), args{ inner: (packet_format,) })] #[br(count(56), args{ inner: (packet_format,) })]
pub weather_forecast_samples: Vec<WeatherForecastSample>, pub weather_forecast_samples: Vec<WeatherForecastSample>,
/// Weather forecast accuracy. /// Weather forecast accuracy.
@ -174,7 +179,7 @@ pub struct F1PacketSessionData {
BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize, BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize,
)] )]
#[br(little, import(packet_format: u16))] #[br(little, import(packet_format: u16))]
pub struct F1PacketLapData { pub struct F1PacketLap {
/// Lap data for all cars on track. /// Lap data for all cars on track.
#[br(count(MAX_NUM_CARS), args{ inner: (packet_format,) })] #[br(count(MAX_NUM_CARS), args{ inner: (packet_format,) })]
pub lap_data: Vec<LapData>, pub lap_data: Vec<LapData>,
@ -192,7 +197,7 @@ pub struct F1PacketLapData {
BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize, BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize,
)] )]
#[br(little, import(_packet_format: u16))] #[br(little, import(_packet_format: u16))]
pub struct F1PacketEventData { pub struct F1PacketEvent {
/// 4-letter event code. /// 4-letter event code.
#[br( #[br(
try_map(|bytes: [u8; 4]| String::from_utf8(bytes.to_vec())), try_map(|bytes: [u8; 4]| String::from_utf8(bytes.to_vec())),
@ -212,18 +217,19 @@ pub struct F1PacketEventData {
little, little,
import(packet_format: u16), import(packet_format: u16),
assert( assert(
num_active_cars <= MAX_NUM_CARS, num_cars <= MAX_NUM_CARS,
"Participants packet has an invalid number of active cars: {}", "Participants packet has an invalid number of cars: {}",
num_active_cars num_cars
) )
)] )]
pub struct F1PacketParticipantsData { pub struct F1PacketParticipants {
/// Number of active cars in the data (no greater than 22) /// Number of active cars in the data (no greater than 22)
#[br(map(u8_to_usize))] #[br(map(u8_to_usize))]
pub num_active_cars: usize, pub num_cars: usize,
/// Data for all participants. /// Data for all participants.
/// Should have a size equal to `num_active_cars`. /// Should have a size equal to
#[br(count(num_active_cars), args{ inner: (packet_format,) })] /// [`num_cars`](field@F1PacketParticipants::num_cars).
#[br(count(num_cars), args{ inner: (packet_format,) })]
pub participants: Vec<ParticipantsData>, pub participants: Vec<ParticipantsData>,
} }
@ -235,7 +241,7 @@ pub struct F1PacketParticipantsData {
BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize, BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize,
)] )]
#[br(little, import(packet_format: u16))] #[br(little, import(packet_format: u16))]
pub struct F1PacketCarSetupsData { pub struct F1PacketCarSetups {
/// Setup data for all cars on track. /// Setup data for all cars on track.
/// Should have a size of 22. /// Should have a size of 22.
#[br(count(MAX_NUM_CARS), args{ inner: (packet_format,) })] #[br(count(MAX_NUM_CARS), args{ inner: (packet_format,) })]
@ -256,14 +262,14 @@ pub struct F1PacketCarSetupsData {
suggested_gear suggested_gear
), ),
)] )]
pub struct F1PacketCarTelemetryData { pub struct F1PacketCarTelemetry {
/// Telemetry data for all cars on track. /// Telemetry data for all cars on track.
/// Should have a size of 22. /// Should have a size of 22.
#[br(count(MAX_NUM_CARS), args{ inner: (packet_format,) })] #[br(count(MAX_NUM_CARS), args{ inner: (packet_format,) })]
pub car_telemetry_data: Vec<CarTelemetryData>, pub car_telemetry_data: Vec<CarTelemetryData>,
/// Index of currently open MFD panel. /// Index of currently open MFD panel.
pub mfd_panel_index: MfdPanelIndex, pub mfd_panel_index: MfdPanelIndex,
/// See [`mfd_panel_index`](field@F1PacketCarTelemetryData::mfd_panel_index). /// See [`mfd_panel_index`](field@F1PacketCarTelemetry::mfd_panel_index).
pub mfd_panel_index_secondary_player: MfdPanelIndex, pub mfd_panel_index_secondary_player: MfdPanelIndex,
/// Suggested gear (0 if no gear suggested). /// Suggested gear (0 if no gear suggested).
pub suggested_gear: i8, pub suggested_gear: i8,
@ -275,21 +281,46 @@ pub struct F1PacketCarTelemetryData {
BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize, BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize,
)] )]
#[br(little, import(packet_format: u16))] #[br(little, import(packet_format: u16))]
pub struct F1PacketCarStatusData { pub struct F1PacketCarStatus {
/// Status data for all cars. Should have a size of 22. /// Status data for all cars. Should have a size of 22.
#[br(count(MAX_NUM_CARS), args{ inner: (packet_format,) })] #[br(count(MAX_NUM_CARS), args{ inner: (packet_format,) })]
pub car_status_data: Vec<CarStatusData>, pub car_status_data: Vec<CarStatusData>,
} }
/// Final classification confirmation at the end of a race.
#[non_exhaustive]
#[derive(
BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize,
)]
#[br(
little,
import(packet_format: u16),
assert(
num_cars <= MAX_NUM_CARS,
"Final classification packet has an invalid number of cars: {}",
num_cars
)
)]
pub struct F1PacketFinalClassification {
/// Number of cars in the final classification (no greater than 22).
#[br(map(u8_to_usize))]
pub num_cars: usize,
/// Final classification data for all cars.
/// Should have a size equal to
/// [`num_cars`](field@F1PacketFinalClassificationData::num_cars).
#[br(count(num_cars), args{ inner: (packet_format,) })]
pub final_classification_data: Vec<FinalClassificationData>,
}
/// Extended motion data for player's car. Available as a: /// Extended motion data for player's car. Available as a:
/// - part of [`F1PacketMotionData`] in the 2022 format /// - part of [`F1PacketMotion`] in the 2022 format
/// - standalone packet from the 2023 format onwards /// - standalone packet from the 2023 format onwards
#[non_exhaustive] #[non_exhaustive]
#[derive( #[derive(
BinRead, PartialEq, PartialOrd, Copy, Clone, Debug, Serialize, Deserialize, BinRead, PartialEq, PartialOrd, Copy, Clone, Debug, Serialize, Deserialize,
)] )]
#[br(little, import(_packet_format: u16))] #[br(little, import(_packet_format: u16))]
pub struct F1PacketMotionExData { pub struct F1PacketMotionEx {
/// Positions of suspension for each wheel. /// Positions of suspension for each wheel.
/// See [`wheel_index`](mod@crate::constants::wheel_index) /// See [`wheel_index`](mod@crate::constants::wheel_index)
/// for wheel order. /// for wheel order.