feat: add 2022 spec event event packet parser

This commit is contained in:
Maciej Pędzich 2025-02-17 21:19:29 +01:00
parent fb83776da5
commit e4728bcf45
Signed by: maciejpedzich
GPG Key ID: CE4A303D84882F0D
6 changed files with 362 additions and 4 deletions

7
Cargo.lock generated
View File

@ -32,6 +32,12 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "bitflags"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
[[package]]
name = "bytemuck"
version = "1.21.0"
@ -49,6 +55,7 @@ name = "f1-game-packet-parser"
version = "0.1.0"
dependencies = [
"binrw",
"bitflags",
"serde",
]

View File

@ -5,4 +5,5 @@ edition = "2021"
[dependencies]
binrw = "0.14"
bitflags = "2.8"
serde = { version = "1.0", features = ["derive"] }

View File

@ -1,6 +1,7 @@
pub mod wheel_index;
use binrw::BinRead;
use bitflags::bitflags;
use serde::{Deserialize, Serialize};
pub(crate) const MAX_NUM_CARS: usize = 22;
@ -414,6 +415,7 @@ pub enum SessionLength {
Full = 7,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
@ -433,6 +435,7 @@ pub enum PitStatus {
InPitArea = 2,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
@ -454,6 +457,7 @@ pub enum DriverStatus {
OnTrack = 4,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
@ -477,3 +481,200 @@ pub enum ResultStatus {
NotClassified = 6,
Retired = 7,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(u8))]
pub enum PenaltyType {
DriveThrough = 0,
StopGo = 1,
GridPenalty = 2,
PenaltyReminder = 3,
TimePenalty = 4,
Warning = 5,
Disqualified = 6,
RemovedFromFormationLap = 7,
ParkedTooLongTimer = 8,
TyreRegulations = 9,
ThisLapInvalidated = 10,
ThisAndNextLapInvalidated = 11,
ThisLapInvalidatedWithoutReason = 12,
ThisAndNextLapInvalidatedWithoutReason = 13,
ThisAndPreviousLapInvalidated = 14,
ThisAndPreviousLapInvalidatedWithoutReason = 15,
Retired = 16,
BlackFlagTimer = 17,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(u8))]
pub enum InfringementType {
BlockingBySlowDriving = 0,
BlockingByWrongWayDriving = 1,
ReversingOffTheStartLine = 2,
BigCollision = 3,
SmallCollision = 4,
CollisionFailedToHandBackPositionSingle = 5,
CollisionFailedToHandBackPositionMultiple = 6,
CornerCuttingGainedTime = 7,
CornerCuttingOvertakeSingle = 8,
CornerCuttingOvertakeMultiple = 9,
CrossedPitExitLane = 10,
IgnoringBlueFlags = 11,
IgnoringYellowFlags = 12,
IgnoringDriveThrough = 13,
TooManyDriveThroughs = 14,
DriveThroughReminderServeWithinNLaps = 15,
DriveThroughReminderServeThisLap = 16,
PitLaneSpeeding = 17,
ParkedForTooLong = 18,
IgnoringTyreRegulations = 19,
TooManyPenalties = 20,
MultipleWarnings = 21,
ApproachingDisqualification = 22,
TyreRegulationsSelectSingle = 23,
TyreRegulationsSelectMultiple = 24,
LapInvalidatedCornerCutting = 25,
LapInvalidatedRunningWide = 26,
CornerCuttingRanWideMinorTimeGain = 27,
CornerCuttingRanWideSignificantTimeGain = 28,
CornerCuttingRanWideExtremeTimeGain = 29,
LapInvalidatedWallRiding = 30,
LapInvalidatedFlashbackUsed = 31,
LapInvalidatedResetToTrack = 32,
BlockingThePitLane = 33,
JumpStart = 34,
SafetyCarCollision = 35,
SafetyCarIllegalOvertake = 36,
SafetyCarExceedingAllowedPace = 37,
VirtualSafetyCarExceedingAllowedPace = 38,
FormationLapBelowAllowedSpeed = 39,
RetiredMechanicalFailure = 40,
RetiredTerminallyDamaged = 41,
SafetyCarFallingTooFarBack = 42,
BlackFlagTimer = 43,
UnservedStopGoPenalty = 44,
UnservedDriveThroughPenalty = 45,
EngineComponentChange = 46,
GearboxChange = 47,
LeagueGridPenalty = 48,
RetryPenalty = 49,
IllegalTimeGain = 50,
MandatoryPitStop = 51,
}
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
Ord,
PartialOrd,
Hash,
Serialize,
Deserialize,
)]
pub struct ButtonStatus(u32);
bitflags! {
impl ButtonStatus: u32 {
/// The "A" button on an Xbox controller
/// or the cross button on a PlayStation controller.
/// Has a value of `0x00000001`.
const A_OR_CROSS = 0x00000001;
/// The "Y" button on an Xbox controller
/// or the triangle button on a PlayStation controller.
/// Has a value of `0x00000002`.
const Y_OR_TRIANGLE = 0x00000002;
/// The "B" button on an Xbox controller
/// or the circle button on a PlayStation controller.
/// Has a value of `0x00000004`.
const B_OR_CIRCLE = 0x00000004;
/// The "X" button on an Xbox controller
/// or the square button on a PlayStation controller.
/// Has a value of `0x00000008`.
const X_OR_SQUARE = 0x00000008;
/// Left directional pad button. Has a value of `0x00000010`.
const DPAD_LEFT = 0x00000010;
/// Right directional pad button. Has a value of `0x00000020`.
const DPAD_RIGHT = 0x00000020;
/// Up directional pad button. Has a value of `0x00000040`.
const DPAD_UP = 0x00000040;
/// Down directional pad button. Has a value of `0x00000080`.
const DPAD_DOWN = 0x00000080;
/// The "Menu" button on an Xbox controller
/// or the "Options" button on a PlayStation controller.
/// Has a value of `0x00000100`.
const MENU_OR_OPTIONS = 0x00000100;
/// Left bumper. Has a value of `0x00000200`.
const LEFT_BUMPER = 0x00000200;
/// Right bumper. Has a value of `0x00000400`.
const RIGHT_BUMPER = 0x00000400;
/// Left trigger. Has a value of `0x00000800`.
const LEFT_TRIGGER = 0x00000800;
/// Right trigger. Has a value of `0x00001000`.
const RIGHT_TRIGGER = 0x00001000;
/// Left analog stick click. Has a value of `0x00002000`.
const LEFT_STICK_CLICK = 0x00002000;
/// Right analog stick click. Has a value of `0x00004000`.
const RIGHT_STICK_CLICK = 0x00004000;
/// Right analog stick left. Has a value of `0x00008000`.
const RIGHT_STICK_LEFT = 0x00008000;
/// Right analog stick right. Has a value of `0x00010000`
const RIGHT_STICK_RIGHT = 0x00010000;
/// Right analog stick up. Has a value of `0x00020000`
const RIGHT_STICK_UP = 0x00020000;
/// Right analog stick down. Has a value of `0x00040000`
const RIGHT_STICK_DOWN = 0x00040000;
/// Special button. Has a value of `0x00080000`.
const SPECIAL = 0x00080000;
/// UDP Action 1. Has a value of `0x00100000`.
const UDP_ACTION_1 = 0x00100000;
/// UDP Action 2. Has a value of `0x00200000`.
const UDP_ACTION_2 = 0x00200000;
/// UDP Action 3. Has a value of `0x00400000`.
const UDP_ACTION_3 = 0x00400000;
/// UDP Action 4. Has a value of `0x00800000`.
const UDP_ACTION_4 = 0x00800000;
/// UDP Action 5. Has a value of `0x01000000`.
const UDP_ACTION_5 = 0x01000000;
/// UDP Action 6. Has a value of `0x02000000`.
const UDP_ACTION_6 = 0x02000000;
/// UDP Action 7. Has a value of `0x04000000`.
const UDP_ACTION_7 = 0x04000000;
/// UDP Action 8. Has a value of `0x08000000`.
const UDP_ACTION_8 = 0x08000000;
/// UDP Action 9. Has a value of `0x10000000`.
const UDP_ACTION_9 = 0x10000000;
/// UDP Action 10. Has a value of `0x20000000`.
const UDP_ACTION_10 = 0x20000000;
/// UDP Action 11. Has a value of `0x40000000`.
const UDP_ACTION_11 = 0x40000000;
/// UDP Action 12. Has a value of `0x80000000`.
const UDP_ACTION_12 = 0x80000000;
}
}

View File

@ -2,7 +2,7 @@ pub mod constants;
pub mod packets;
use crate::constants::PacketId;
use crate::packets::{F1PacketMotionData, F1PacketSessionData};
use crate::packets::{F1PacketEventData, F1PacketLapData, F1PacketMotionData, F1PacketSessionData};
use binrw::io::Cursor;
use binrw::{BinRead, BinReaderExt, BinResult};
@ -75,4 +75,10 @@ pub struct F1PacketBody {
/// Data about the ongoing session.
#[br(if(packet_id == PacketId::Session), args(packet_format))]
pub session: Option<F1PacketSessionData>,
/// Lap data for all cars on track.
#[br(if(packet_id == PacketId::LapData), args(packet_format))]
pub lap: Option<F1PacketLapData>,
/// Details of events that happen during the course of a session.
#[br(if(packet_id == PacketId::Event), args(packet_format))]
pub event: Option<F1PacketEventData>,
}

125
src/packets/event.rs Normal file
View File

@ -0,0 +1,125 @@
use super::u8_to_usize;
use crate::constants::{ButtonStatus, InfringementType, PenaltyType};
use binrw::BinRead;
use serde::{Deserialize, Serialize};
#[non_exhaustive]
#[derive(
BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize,
)]
#[br(little)]
pub enum EventDataDetails {
/// Sent when the session starts.
#[br(magic = b"SSTA")]
SessionStarted,
/// Sent when the session ends.
#[br(magic = b"SEND")]
SessionEnded,
/// Sent when a driver achieves the fastest lap.
#[br(magic = b"FTLP")]
FastestLap {
/// Index of the car that's achieved the fastest lap.
#[br(map(u8_to_usize))]
vehicle_index: usize,
/// Lap time in seconds.
lap_time: f32,
},
/// Sent when a driver retires.
#[br(magic = b"RTMT")]
Retirement {
/// Index of the retiring car.
#[br(map(u8_to_usize))]
vehicle_index: usize,
},
/// Sent when race control enable DRS.
#[br(magic = b"DRSE")]
DrsEnabled,
/// Sent when race control disable DRS.
#[br(magic = b"DRSD")]
DrsDisabled,
/// Sent when your teammate enters the pit lane.
#[br(magic = b"TMPT")]
TeamMateInPits {
/// Index of teammate's car.
#[br(map(u8_to_usize))]
vehicle_index: usize,
},
/// Sent when the chequered flag has been waved.
#[br(magic = b"CHQF")]
ChequeredFlag,
/// Sent when the race winner is announced.
#[br(magic = b"RCWN")]
RaceWinner {
/// Index of race winner's car.
#[br(map(u8_to_usize))]
vehicle_index: usize,
},
/// Sent when a penalty has been issued.
#[br(magic = b"PENA")]
Penalty {
/// Penalty type.
penalty_type: PenaltyType,
/// Infringement type.
infringement_type: InfringementType,
/// Index of the car the penalty is applied to.
#[br(map(u8_to_usize))]
vehicle_index: usize,
/// Index of the other car involved.
#[br(map(u8_to_usize))]
other_vehicle_index: usize,
/// Time gained/spent doing the action in seconds.
time: u8,
/// Number of the lap the infringement occurred on.
lap_num: u8,
/// Number of places gained by this infringement.
places_gained: u8,
},
/// Sent when a speed trap is triggered.
#[br(magic = b"SPTP")]
SpeedTrap {
/// Index of the car that's triggered the speed trap.
#[br(map(u8_to_usize))]
vehicle_index: usize,
/// Top speed achieved in kilometres per hour.
speed: f32,
},
/// Sent when a start light is lit.
#[br(magic = b"STLG")]
StartLights {
/// Number of lights showing.
num_lights: u8,
},
/// "It's lights out, and away we go!"
#[br(magic = b"LGOT")]
LightsOut,
/// Sent when a driver has served a drive-through penalty.
#[br(magic = b"DTSV")]
DriveThroughServed {
/// Index of the vehicle serving the penalty.
#[br(map(u8_to_usize))]
vehicle_index: usize,
},
/// Sent when a driver has served a stop-go penalty.
#[br(magic = b"SGSV")]
StopGoServed {
/// Index of the vehicle serving the penalty.
#[br(map(u8_to_usize))]
vehicle_index: usize,
},
/// Sent when a flashback is activated.
#[br(magic = b"FLBK")]
Flashback {
/// Frame identifier that's been flashed back to.
frame_identifier: u32,
/// Session time that's been flashed back to.
flashback_session_time: f32,
},
/// Sent when the button status has changed.
#[br(magic = b"BUTN")]
Buttons {
/// Bit flags specifying which buttons are currently pressed.
#[br(map(|bits: u32| ButtonStatus::from_bits_truncate(bits)))]
button_status: ButtonStatus
},
}

View File

@ -1,8 +1,7 @@
pub mod motion;
pub mod session;
pub mod lap_data;
use crate::packets::motion::CarMotionData;
pub mod event;
use crate::constants::{
BrakingAssist, DynamicRacingLine,
@ -11,9 +10,11 @@ use crate::constants::{
TrackId, Weather, MAX_AI_DIFFICULTY, MAX_NUM_CARS, MAX_NUM_MARSHAL_ZONES,
MAX_NUM_WEATHER_FORECAST_SAMPLES,
};
use crate::packets::event::EventDataDetails;
use crate::packets::lap_data::LapData;
use crate::packets::motion::CarMotionData;
use crate::packets::session::{MarshalZone, WeatherForecastSample};
use crate::packets::lap_data::LapData;
use binrw::BinRead;
use serde::{Deserialize, Serialize};
use std::string::FromUtf8Error;
@ -180,6 +181,23 @@ pub struct F1PacketLapData {
pub time_trial_rival_car_index: usize,
}
/// Various notable events that happen during a session.
#[non_exhaustive]
#[derive(
BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize,
)]
#[br(little, import(_packet_format: u16))]
pub struct F1PacketEventData {
/// 4-letter event code.
#[br(
try_map(|bytes: [u8; 4]| String::from_utf8(bytes.to_vec())),
restore_position
)]
pub event_string_code: String,
/// Extra data for this event.
pub event_details: EventDataDetails,
}
/// Extended motion data for player's car. Available as a:
/// - part of [`F1PacketMotionData`] in the 2022 format
/// - standalone packet from the 2023 format onwards