Crate f1_game_packet_parser

Source
Expand description

Convert binary data from F1 24, F1 23, and F1 22 UDP telemetry into organised structs.

§Getting started

Add f1-game-packet-parser to your project by running the following command:

cargo add f1-game-packet-parser

§Basic UDP client

This crate doesn’t provide a UDP client out of the box. Here’s how to write one that will parse and pretty-print incoming packets:

use f1_game_packet_parser::parse;
use std::error::Error;
use std::net::UdpSocket;

fn main() -> Result<(), Box<dyn Error>> {
    // This IP and port should be set in the game's options by default.
    let socket = UdpSocket::bind("127.0.0.1:20777")?;
    let mut buf = [0u8; 1464];

    loop {
        // Receive raw packet data from the game.
        // The buf array should be large enough for all types of packets.
        let (amt, _) = socket.recv_from(&mut buf)?;

        // Convert received bytes to an F1Packet struct and print it.
        let packet = parse(&buf[..amt])?;
        println!("{:#?}", packet);
    }
}

§Determining a packet’s type and extracting its payload

An F1Packet consists of a universal header and an Option field for a payload of every single packet type. Only one of these can be set to Some for a given F1Packet instance.

Therefore, you can use the following if-else-if chain to differentiate between all packet types and extract their payloads. Of course, you can remove the branches you don’t need.

use f1_game_packet_parser::parse;

let placeholder_data = include_bytes!("placeholder.bin");
let packet = parse(placeholder_data)?;

if let Some(motion) = &packet.motion {
    // Do whatever with motion.
} else if let Some(session) = &packet.session {
    // Do whatever with session.
} else if let Some(laps) = &packet.laps {
    // Do whatever with laps.
} else if let Some(event) = &packet.event {
    // Do whatever with event.
} else if let Some(participants) = &packet.participants {
    // Do whatever with participants.
} else if let Some(car_setups) = &packet.car_setups {
    // Do whatever with car_setups.
} else if let Some(car_telemetry) = &packet.car_telemetry {
    // Do whatever with car_telemetry.
} else if let Some(car_status) = &packet.car_status {
    // Do whatever with car_status.
} else if let Some(final_classification) = &packet.final_classification {
    // Do whatever with final_classification.
} else if let Some(lobby) = &packet.lobby {
    // Do whatever with lobby.
} else if let Some(car_damage) = &packet.car_damage {
    // Do whatever with car_damage.
} else if let Some(session_history) = &packet.session_history {
    // Do whatever with session_history.
} else if let Some(tyre_sets) = &packet.tyre_sets {
    // Available from the 2023 format onwards.
    // Do whatever with tyre_sets.
} else if let Some(motion_ex) = &packet.motion_ex {
    // Available from the 2023 format onwards.
    // Do whatever with motion_ex.
} else if let Some(time_trial) = &packet.time_trial {
    // Available from the 2024 format onwards.
    // Do whatever with time_trial.
}

§Working with event packets

F1PacketEvent is unique among other kinds of packets. Its payload consists of a 4-letter code that determines the type of the event, followed by optional details about this event.

These extra details are represented by the EventDetails enum (even if a certain event doesn’t come with additional data). You can import the enum and use a matcher to determine an event’s type and extract its payload (if available) like so:

use f1_game_packet_parser::packets::event::EventDetails;
use f1_game_packet_parser::parse;

let placeholder_data = include_bytes!("placeholder.bin");
let packet = parse(placeholder_data)?;

if let Some(event) = &packet.event {
    match event.details {
        /// Event with no extra details.
        EventDetails::LightsOut => {
            println!("It's lights out, and away we go!");
        }
        /// You can skip the details if you don't need them.
        EventDetails::Flashback { .. } => {
            println!("Flashback has been triggered!");
        }
        /// Extracting details from an event.
        EventDetails::RaceWinner { vehicle_index } => {
            println!(
                "Driver at index {} is the winner!",
                vehicle_index
            );
        }
        _ => (),
    }
}

§Working with bitmaps

There are 3 fields that use a bitflags-powered bitmap struct:

Each bitmap struct is publicly available via the constants module and comes with a handful of constants representing specific bit values, as well as methods and operator overloads for common bit operations.

Here’s an example that checks if a given binary file is a car telemetry packet. If so, it will grab player car’s telemetry data and determine whether the revs are high, medium or low based on the specific bit values being set.

use f1_game_packet_parser::constants::RevLights;
use f1_game_packet_parser::parse;

let placeholder_data = include_bytes!("placeholder.bin");
let packet = parse(placeholder_data)?;
let player_car_index = packet.header.player_car_index;

if let Some(car_telemetry) = &packet.car_telemetry {
    let player = car_telemetry.data[player_car_index];
    let is_high_rev =
        player.rev_lights_bit_value.contains(RevLights::RIGHT_1);
    let is_medium_rev =
        player.rev_lights_bit_value.contains(RevLights::MIDDLE_1);

    let revs_desc = if is_high_rev {
        "High"
    } else if is_medium_rev {
        "Medium"
    } else {
        "Low"
    };

    println!("{} revs", revs_desc);
}

See the respective structs’ documentation for a complete list of constants and methods.

Modules§

constants
Contains appendix constants and enums for various packet-specific struct field values.
packets
Contains structures for each kind of packet payload and submodules for packet-specific structs.

Structs§

F1Packet
Structured representation of raw F1 game packet data that’s returned as a successful result of the parse function.
F1PacketHeader
F1 game packet’s header. It contains metadata about the game, the ongoing session, the frame this packet was sent on, and player car indexes.

Functions§

parse
Attempts to extract F1 game packet data from a byte buffer (such as a Vec<u8>, [u8; N], or &[u8]).