Created
May 3, 2025 19:59
-
-
Save nicosingh/a8d7214392db03cea6f5ac296d8bcf1a to your computer and use it in GitHub Desktop.
F1 24 UDP Client in Python
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import socket | |
import ctypes | |
from enum import IntEnum, Enum | |
import time | |
class PacketID(IntEnum): | |
MOTION = 0 | |
SESSION = 1 | |
LAP_DATA = 2 | |
EVENT = 3 | |
PARTICIPANTS = 4 | |
CAR_SETUPS = 5 | |
CAR_TELEMETRY = 6 | |
CAR_STATUS = 7 | |
FINAL_CLASSIFICATION = 8 | |
LOBBY_INFO = 9 | |
CAR_DAMAGE = 10 | |
SESSION_HISTORY = 11 | |
TYRE_SETS = 12 | |
MOTION_EX = 13 | |
TIME_TRIAL = 14 | |
class Packet(ctypes.Structure): | |
"""Base class for all F1 packet types""" | |
_pack_ = 1 | |
@classmethod | |
def from_buffer(cls, data, offset=0): | |
return cls.from_buffer_copy(data, offset) | |
def to_dict(self): | |
"""Convert the structure to a nested dictionary""" | |
result = {} | |
for field_name, _ in self._fields_: | |
value = getattr(self, field_name) | |
# Handle arrays | |
if hasattr(value, '_length_'): | |
result[field_name] = [item.to_dict() if hasattr(item, 'to_dict') else item for item in value] | |
# Handle nested structures | |
elif hasattr(value, '_fields_'): | |
result[field_name] = value.to_dict() if hasattr(value, 'to_dict') else value | |
# Handle normal fields | |
else: | |
result[field_name] = value | |
return result | |
class PacketHeader(Packet): | |
_fields_ = [ | |
("m_packet_format", ctypes.c_uint16), | |
("m_game_year", ctypes.c_uint8), | |
("m_game_major_version", ctypes.c_uint8), | |
("m_game_minor_version", ctypes.c_uint8), | |
("m_packet_version", ctypes.c_uint8), | |
("m_packet_id", ctypes.c_uint8), | |
("m_session_uid", ctypes.c_uint64), | |
("m_session_time", ctypes.c_float), | |
("m_frame_identifier", ctypes.c_uint32), | |
("m_overall_frame_identifier", ctypes.c_uint32), | |
("m_player_car_index", ctypes.c_uint8), | |
("m_secondary_player_car_index", ctypes.c_uint8) | |
] | |
class CarMotionData(Packet): | |
_fields_ = [ | |
("m_world_position_x", ctypes.c_float), # World space X position | |
("m_world_position_y", ctypes.c_float), # World space Y position | |
("m_world_position_z", ctypes.c_float), # World space Z position | |
("m_world_velocity_x", ctypes.c_float), # Velocity in world space X | |
("m_world_velocity_y", ctypes.c_float), # Velocity in world space Y | |
("m_world_velocity_z", ctypes.c_float), # Velocity in world space Z | |
("m_world_forward_dir_x", ctypes.c_int16), # World space forward X direction (normalised) | |
("m_world_forward_dir_y", ctypes.c_int16), # World space forward Y direction (normalised) | |
("m_world_forward_dir_z", ctypes.c_int16), # World space forward Z direction (normalised) | |
("m_world_right_dir_x", ctypes.c_int16), # World space right X direction (normalised) | |
("m_world_right_dir_y", ctypes.c_int16), # World space right Y direction (normalised) | |
("m_world_right_dir_z", ctypes.c_int16), # World space right Z direction (normalised) | |
("m_g_force_lateral", ctypes.c_float), # Lateral G-Force component | |
("m_g_force_longitudinal", ctypes.c_float), # Longitudinal G-Force component | |
("m_g_force_vertical", ctypes.c_float), # Vertical G-Force component | |
("m_yaw", ctypes.c_float), # Yaw angle in radians | |
("m_pitch", ctypes.c_float), # Pitch angle in radians | |
("m_roll", ctypes.c_float), # Roll angle in radians | |
] | |
class PacketMotionData(Packet): | |
_fields_ = [ | |
("m_header", PacketHeader), # Header | |
("m_car_motion_data", CarMotionData * 22), # Data for all cars on track | |
] | |
class MarshalZone(Packet): | |
_fields_ = [ | |
("m_zone_start", ctypes.c_float), # Fraction (0..1) of way through the lap the marshal zone starts | |
("m_zone_flag", ctypes.c_int8), # -1 = invalid/unknown, 0 = none, 1 = green, 2 = blue, 3 = yellow | |
] | |
class WeatherForecastSample(Packet): | |
_fields_ = [ | |
("m_session_type", ctypes.c_uint8), # 0 = unknown, see appendix | |
("m_time_offset", ctypes.c_uint8), # Time in minutes the forecast is for | |
("m_weather", ctypes.c_uint8), # Weather - 0 = clear, 1 = light cloud, 2 = overcast, 3 = light rain, 4 = heavy rain, 5 = storm | |
("m_track_temperature", ctypes.c_int8), # Track temp. in degrees Celsius | |
("m_track_temperature_change", ctypes.c_int8), # Track temp. change – 0 = up, 1 = down, 2 = no change | |
("m_air_temperature", ctypes.c_int8), # Air temp. in degrees celsius | |
("m_air_temperature_change", ctypes.c_int8), # Air temp. change – 0 = up, 1 = down, 2 = no change | |
("m_rain_percentage", ctypes.c_uint8), # Rain percentage (0-100) | |
] | |
class PacketSessionData(Packet): | |
_fields_ = [ | |
("m_header", PacketHeader), # Header | |
("m_weather", ctypes.c_uint8), # Weather - 0 = clear, 1 = light cloud, 2 = overcast, 3 = light rain, 4 = heavy rain, 5 = storm | |
("m_track_temperature", ctypes.c_int8), # Track temp. in degrees celsius | |
("m_air_temperature", ctypes.c_int8), # Air temp. in degrees celsius | |
("m_total_laps", ctypes.c_uint8), # Total number of laps in this race | |
("m_track_length", ctypes.c_uint16), # Track length in metres | |
("m_session_type", ctypes.c_uint8), # 0 = unknown, see appendix | |
("m_track_id", ctypes.c_int8), # -1 for unknown, see appendix | |
("m_formula", ctypes.c_uint8), # Formula, 0 = F1 Modern, 1 = F1 Classic, 2 = F2, 3 = F1 Generic, 4 = Beta, 6 = Esports, 8 = F1 World, 9 = F1 Elimination | |
("m_session_time_left", ctypes.c_uint16), # Time left in session in seconds | |
("m_session_duration", ctypes.c_uint16), # Session duration in seconds | |
("m_pit_speed_limit", ctypes.c_uint8), # Pit speed limit in kilometres per hour | |
("m_game_paused", ctypes.c_uint8), # Whether the game is paused – network game only | |
("m_is_spectating", ctypes.c_uint8), # Whether the player is spectating | |
("m_spectator_car_index", ctypes.c_uint8), # Index of the car being spectated | |
("m_sli_pro_native_support", ctypes.c_uint8), # SLI Pro support, 0 = inactive, 1 = active | |
("m_num_marshal_zones", ctypes.c_uint8), # Number of marshal zones to follow | |
("m_marshal_zones", MarshalZone * 21), # List of marshal zones – max 21 | |
("m_safety_car_status", ctypes.c_uint8), # 0 = no safety car, 1 = full, 2 = virtual, 3 = formation lap | |
("m_network_game", ctypes.c_uint8), # 0 = offline, 1 = online | |
("m_num_weather_forecast_samples", ctypes.c_uint8), # Number of weather samples to follow | |
("m_weather_forecast_samples", WeatherForecastSample * 64), # Array of weather forecast samples | |
("m_forecast_accuracy", ctypes.c_uint8), # 0 = Perfect, 1 = Approximate | |
("m_ai_difficulty", ctypes.c_uint8), # AI Difficulty rating – 0-110 | |
("m_season_link_identifier", ctypes.c_uint32), # Identifier for season - persists across saves | |
("m_weekend_link_identifier", ctypes.c_uint32), # Identifier for weekend - persists across saves | |
("m_session_link_identifier", ctypes.c_uint32), # Identifier for session - persists across saves | |
("m_pit_stop_window_ideal_lap", ctypes.c_uint8), # Ideal lap to pit on for current strategy (player) | |
("m_pit_stop_window_latest_lap", ctypes.c_uint8), # Latest lap to pit on for current strategy (player) | |
("m_pit_stop_rejoin_position", ctypes.c_uint8), # Predicted position to rejoin at (player) | |
("m_steering_assist", ctypes.c_uint8), # 0 = off, 1 = on | |
("m_braking_assist", ctypes.c_uint8), # 0 = off, 1 = low, 2 = medium, 3 = high | |
("m_gearbox_assist", ctypes.c_uint8), # 1 = manual, 2 = manual & suggested gear, 3 = auto | |
("m_pit_assist", ctypes.c_uint8), # 0 = off, 1 = on | |
("m_pit_release_assist", ctypes.c_uint8), # 0 = off, 1 = on | |
("m_ers_assist", ctypes.c_uint8), # 0 = off, 1 = on | |
("m_drs_assist", ctypes.c_uint8), # 0 = off, 1 = on | |
("m_dynamic_racing_line", ctypes.c_uint8), # 0 = off, 1 = corners only, 2 = full | |
("m_dynamic_racing_line_type", ctypes.c_uint8), # 0 = 2D, 1 = 3D | |
("m_game_mode", ctypes.c_uint8), # Game mode id - see appendix | |
("m_rule_set", ctypes.c_uint8), # Ruleset - see appendix | |
("m_time_of_day", ctypes.c_uint32), # Local time of day - minutes since midnight | |
("m_session_length", ctypes.c_uint8), # 0 = None, 2 = Very Short, 3 = Short, 4 = Medium, 5 = Medium Long, 6 = Long, 7 = Full | |
("m_speed_units_lead_player", ctypes.c_uint8), # 0 = MPH, 1 = KPH | |
("m_temperature_units_lead_player", ctypes.c_uint8), # 0 = Celsius, 1 = Fahrenheit | |
("m_speed_units_secondary_player", ctypes.c_uint8), # 0 = MPH, 1 = KPH | |
("m_temperature_units_secondary_player", ctypes.c_uint8), # 0 = Celsius, 1 = Fahrenheit | |
("m_num_safety_car_periods", ctypes.c_uint8), # Number of safety cars called during session | |
("m_num_virtual_safety_car_periods", ctypes.c_uint8), # Number of virtual safety cars called | |
("m_num_red_flag_periods", ctypes.c_uint8), # Number of red flags called during session | |
("m_equal_car_performance", ctypes.c_uint8), # 0 = Off, 1 = On | |
("m_recovery_mode", ctypes.c_uint8), # 0 = None, 1 = Flashbacks, 2 = Auto-recovery | |
("m_flashback_limit", ctypes.c_uint8), # 0 = Low, 1 = Medium, 2 = High, 3 = Unlimited | |
("m_surface_type", ctypes.c_uint8), # 0 = Simplified, 1 = Realistic | |
("m_low_fuel_mode", ctypes.c_uint8), # 0 = Easy, 1 = Hard | |
("m_race_starts", ctypes.c_uint8), # 0 = Manual, 1 = Assisted | |
("m_tyre_temperature", ctypes.c_uint8), # 0 = Surface only, 1 = Surface & Carcass | |
("m_pit_lane_tyre_sim", ctypes.c_uint8), # 0 = On, 1 = Off | |
("m_car_damage", ctypes.c_uint8), # 0 = Off, 1 = Reduced, 2 = Standard, 3 = Simulation | |
("m_car_damage_rate", ctypes.c_uint8), # 0 = Reduced, 1 = Standard, 2 = Simulation | |
("m_collisions", ctypes.c_uint8), # 0 = Off, 1 = Player-to-Player Off, 2 = On | |
("m_collisions_off_for_first_lap_only", ctypes.c_uint8), # 0 = Disabled, 1 = Enabled | |
("m_mp_unsafe_pit_release", ctypes.c_uint8), # 0 = On, 1 = Off (Multiplayer) | |
("m_mp_off_for_griefing", ctypes.c_uint8), # 0 = Disabled, 1 = Enabled (Multiplayer) | |
("m_corner_cutting_stringency", ctypes.c_uint8), # 0 = Regular, 1 = Strict | |
("m_parc_ferme_rules", ctypes.c_uint8), # 0 = Off, 1 = On | |
("m_pit_stop_experience", ctypes.c_uint8), # 0 = Automatic, 1 = Broadcast, 2 = Immersive | |
("m_safety_car", ctypes.c_uint8), # 0 = Off, 1 = Reduced, 2 = Standard, 3 = Increased | |
("m_safety_car_experience", ctypes.c_uint8), # 0 = Broadcast, 1 = Immersive | |
("m_formation_lap", ctypes.c_uint8), # 0 = Off, 1 = On | |
("m_formation_lap_experience", ctypes.c_uint8), # 0 = Broadcast, 1 = Immersive | |
("m_red_flags", ctypes.c_uint8), # 0 = Off, 1 = Reduced, 2 = Standard, 3 = Increased | |
("m_affects_licence_level_solo", ctypes.c_uint8), # 0 = Off, 1 = On | |
("m_affects_licence_level_mp", ctypes.c_uint8), # 0 = Off, 1 = On | |
("m_num_sessions_in_weekend", ctypes.c_uint8), # Number of session in following array | |
("m_weekend_structure", ctypes.c_uint8 * 12), # List of session types to show weekend structure - see appendix for types | |
("m_sector2_lap_distance_start", ctypes.c_float), # Distance in m around track where sector 2 starts | |
("m_sector3_lap_distance_start", ctypes.c_float), # Distance in m around track where sector 3 starts | |
] | |
class LapData(Packet): | |
_fields_ = [ | |
("m_last_lap_time_in_ms", ctypes.c_uint32), # Last lap time in milliseconds | |
("m_current_lap_time_in_ms", ctypes.c_uint32), # Current time around the lap in milliseconds | |
("m_sector1_time_ms_part", ctypes.c_uint16), # Sector 1 time milliseconds part | |
("m_sector1_time_minutes_part", ctypes.c_uint8), # Sector 1 whole minute part | |
("m_sector2_time_ms_part", ctypes.c_uint16), # Sector 2 time milliseconds part | |
("m_sector2_time_minutes_part", ctypes.c_uint8), # Sector 2 whole minute part | |
("m_delta_to_car_in_front_ms_part", ctypes.c_uint16), # Time delta to car in front milliseconds part | |
("m_delta_to_car_in_front_minutes_part", ctypes.c_uint8), # Time delta to car in front whole minute part | |
("m_delta_to_race_leader_ms_part", ctypes.c_uint16), # Time delta to race leader milliseconds part | |
("m_delta_to_race_leader_minutes_part", ctypes.c_uint8), # Time delta to race leader whole minute part | |
("m_lap_distance", ctypes.c_float), # Distance vehicle is around current lap in metres | |
("m_total_distance", ctypes.c_float), # Total distance travelled in session in metres | |
("m_safety_car_delta", ctypes.c_float), # Delta in seconds for safety car | |
("m_car_position", ctypes.c_uint8), # Car race position | |
("m_current_lap_num", ctypes.c_uint8), # Current lap number | |
("m_pit_status", ctypes.c_uint8), # 0 = none, 1 = pitting, 2 = in pit area | |
("m_num_pit_stops", ctypes.c_uint8), # Number of pit stops taken in this race | |
("m_sector", ctypes.c_uint8), # 0 = sector1, 1 = sector2, 2 = sector3 | |
("m_current_lap_invalid", ctypes.c_uint8), # Current lap invalid - 0 = valid, 1 = invalid | |
("m_penalties", ctypes.c_uint8), # Accumulated time penalties in seconds to be added | |
("m_total_warnings", ctypes.c_uint8), # Accumulated number of warnings issued | |
("m_corner_cutting_warnings", ctypes.c_uint8), # Accumulated number of corner cutting warnings issued | |
("m_num_unserved_drive_through_pens", ctypes.c_uint8), # Num drive through pens left to serve | |
("m_num_unserved_stop_go_pens", ctypes.c_uint8), # Num stop go pens left to serve | |
("m_grid_position", ctypes.c_uint8), # Grid position the vehicle started the race in | |
("m_driver_status", ctypes.c_uint8), # Status of driver - 0 = in garage, 1 = flying lap, etc | |
("m_result_status", ctypes.c_uint8), # Result status - 0 = invalid, 1 = inactive, etc | |
("m_pit_lane_timer_active", ctypes.c_uint8), # Pit lane timing, 0 = inactive, 1 = active | |
("m_pit_lane_time_in_lane_in_ms", ctypes.c_uint16), # If active, the current time spent in the pit lane in ms | |
("m_pit_stop_timer_in_ms", ctypes.c_uint16), # Time of the actual pit stop in ms | |
("m_pit_stop_should_serve_pen", ctypes.c_uint8), # Whether the car should serve a penalty at this stop | |
("m_speed_trap_fastest_speed", ctypes.c_float), # Fastest speed through speed trap for this car in kmph | |
("m_speed_trap_fastest_lap", ctypes.c_uint8), # Lap no the fastest speed was achieved, 255 = not set | |
] | |
class PacketLapData(Packet): | |
_fields_ = [ | |
("m_header", PacketHeader), # Header | |
("m_lap_data", LapData * 22), # Lap data for all cars on track | |
("m_time_trial_pb_car_idx", ctypes.c_uint8), # Index of Personal Best car in time trial (255 if invalid) | |
("m_time_trial_rival_car_idx", ctypes.c_uint8), # Index of Rival car in time trial (255 if invalid) | |
] | |
class FastestLapData(Packet): | |
_fields_ = [ | |
("vehicle_idx", ctypes.c_uint8), # Vehicle index of car achieving fastest lap | |
("lap_time", ctypes.c_float) # Lap time in seconds | |
] | |
class RetirementData(Packet): | |
_fields_ = [ | |
("vehicle_idx", ctypes.c_uint8) # Vehicle index of car retiring | |
] | |
class TeamMateInPitsData(Packet): | |
_fields_ = [ | |
("vehicle_idx", ctypes.c_uint8) # Vehicle index of team mate | |
] | |
class RaceWinnerData(Packet): | |
_fields_ = [ | |
("vehicle_idx", ctypes.c_uint8) # Vehicle index of the race winner | |
] | |
class PenaltyData(Packet): | |
_fields_ = [ | |
("penalty_type", ctypes.c_uint8), # Penalty type – see Appendices | |
("infringement_type", ctypes.c_uint8), # Infringement type – see Appendices | |
("vehicle_idx", ctypes.c_uint8), # Vehicle index of the car the penalty is applied to | |
("other_vehicle_idx", ctypes.c_uint8), # Vehicle index of the other car involved | |
("time", ctypes.c_uint8), # Time gained, or time spent doing action in seconds | |
("lap_num", ctypes.c_uint8), # Lap the penalty occurred on | |
("places_gained", ctypes.c_uint8) # Number of places gained by this | |
] | |
class SpeedTrapData(Packet): | |
_fields_ = [ | |
("vehicle_idx", ctypes.c_uint8), # Vehicle index triggering speed trap | |
("speed", ctypes.c_float), # Top speed achieved in kph | |
("is_overall_fastest_in_session", ctypes.c_uint8), # Overall fastest in session (1) or not (0) | |
("is_driver_fastest_in_session", ctypes.c_uint8), # Fastest for driver in session (1) or not (0) | |
("fastest_vehicle_idx_in_session", ctypes.c_uint8), # Vehicle index of fastest in session | |
("fastest_speed_in_session", ctypes.c_float) # Speed of the fastest vehicle in session | |
] | |
class StartLightsData(Packet): | |
_fields_ = [ | |
("num_lights", ctypes.c_uint8) # Number of lights showing | |
] | |
class DriveThroughPenaltyServedData(Packet): | |
_fields_ = [ | |
("vehicle_idx", ctypes.c_uint8) # Vehicle index serving drive through | |
] | |
class StopGoPenaltyServedData(Packet): | |
_fields_ = [ | |
("vehicle_idx", ctypes.c_uint8) # Vehicle index serving stop go | |
] | |
class FlashbackData(Packet): | |
_fields_ = [ | |
("flashback_frame_identifier", ctypes.c_uint32), # Frame identifier flashed back to | |
("flashback_session_time", ctypes.c_float) # Session time flashed back to | |
] | |
class ButtonsData(Packet): | |
_fields_ = [ | |
("button_status", ctypes.c_uint32) # Bit flags for button presses - see appendices | |
] | |
class OvertakeData(Packet): | |
_fields_ = [ | |
("overtaking_vehicle_idx", ctypes.c_uint8), # Vehicle index overtaking | |
("being_overtaken_vehicle_idx", ctypes.c_uint8) # Vehicle index being overtaken | |
] | |
class SafetyCarData(Packet): | |
_fields_ = [ | |
("safety_car_type", ctypes.c_uint8), # 0=None, 1=Full, 2=Virtual, 3=Formation Lap | |
("event_type", ctypes.c_uint8) # 0=Deployed, 1=Returning, 2=Returned, 3=Resume Race | |
] | |
class CollisionData(Packet): | |
_fields_ = [ | |
("vehicle1_idx", ctypes.c_uint8), # First vehicle in collision | |
("vehicle2_idx", ctypes.c_uint8) # Second vehicle in collision | |
] | |
class EventDataDetails(ctypes.Union): | |
_fields_ = [ | |
("fastest_lap", FastestLapData), | |
("retirement", RetirementData), | |
("team_mate_in_pits", TeamMateInPitsData), | |
("race_winner", RaceWinnerData), | |
("penalty", PenaltyData), | |
("speed_trap", SpeedTrapData), | |
("start_lights", StartLightsData), | |
("drive_through_penalty_served", DriveThroughPenaltyServedData), | |
("stop_go_penalty_served", StopGoPenaltyServedData), | |
("flashback", FlashbackData), | |
("buttons", ButtonsData), | |
("overtake", OvertakeData), | |
("safety_car", SafetyCarData), | |
("collision", CollisionData) | |
] | |
class PacketEventData(Packet): | |
_fields_ = [ | |
("m_header", PacketHeader), # Header | |
("m_event_string_code", ctypes.c_char * 4), # Event string code, see below | |
("m_event_details", EventDataDetails) # Event details - interpret based on event code | |
] | |
class ParticipantData(Packet): | |
_fields_ = [ | |
("m_ai_controlled", ctypes.c_uint8), # Whether the vehicle is AI (1) or Human (0) controlled | |
("m_driver_id", ctypes.c_uint8), # Driver id - see appendix, 255 if network human | |
("m_network_id", ctypes.c_uint8), # Network id – unique identifier for network players | |
("m_team_id", ctypes.c_uint8), # Team id - see appendix | |
("m_my_team", ctypes.c_uint8), # My team flag – 1 = My Team, 0 = otherwise | |
("m_race_number", ctypes.c_uint8), # Race number of the car | |
("m_nationality", ctypes.c_uint8), # Nationality of the driver | |
("m_name", ctypes.c_char * 48), # Name of participant in UTF-8 format – null terminated | |
("m_your_telemetry", ctypes.c_uint8), # The player's UDP setting, 0 = restricted, 1 = public | |
("m_show_online_names", ctypes.c_uint8), # The player's show online names setting, 0 = off, 1 = on | |
("m_tech_level", ctypes.c_uint16), # F1 World tech level | |
("m_platform", ctypes.c_uint8), # 1 = Steam, 3 = PlayStation, 4 = Xbox, 6 = Origin, 255 = unknown | |
] | |
class PacketParticipantsData(Packet): | |
_fields_ = [ | |
("m_header", PacketHeader), # Header | |
("m_num_active_cars", ctypes.c_uint8), # Number of active cars in the data – should match number of cars on HUD | |
("m_participants", ParticipantData * 22), | |
] | |
class CarSetupData(Packet): | |
_fields_ = [ | |
("m_front_wing", ctypes.c_uint8), # Front wing aero | |
("m_rear_wing", ctypes.c_uint8), # Rear wing aero | |
("m_on_throttle", ctypes.c_uint8), # Differential adjustment on throttle (percentage) | |
("m_off_throttle", ctypes.c_uint8), # Differential adjustment off throttle (percentage) | |
("m_front_camber", ctypes.c_float), # Front camber angle (suspension geometry) | |
("m_rear_camber", ctypes.c_float), # Rear camber angle (suspension geometry) | |
("m_front_toe", ctypes.c_float), # Front toe angle (suspension geometry) | |
("m_rear_toe", ctypes.c_float), # Rear toe angle (suspension geometry) | |
("m_front_suspension", ctypes.c_uint8), # Front suspension | |
("m_rear_suspension", ctypes.c_uint8), # Rear suspension | |
("m_front_anti_roll_bar", ctypes.c_uint8), # Front anti-roll bar | |
("m_rear_anti_roll_bar", ctypes.c_uint8), # Front anti-roll bar | |
("m_front_suspension_height", ctypes.c_uint8), # Front ride height | |
("m_rear_suspension_height", ctypes.c_uint8), # Rear ride height | |
("m_brake_pressure", ctypes.c_uint8), # Brake pressure (percentage) | |
("m_brake_bias", ctypes.c_uint8), # Brake bias (percentage) | |
("m_engine_braking", ctypes.c_uint8), # Engine braking (percentage) | |
("m_rear_left_tyre_pressure", ctypes.c_float), # Rear left tyre pressure (PSI) | |
("m_rear_right_tyre_pressure", ctypes.c_float), # Rear right tyre pressure (PSI) | |
("m_front_left_tyre_pressure", ctypes.c_float), # Front left tyre pressure (PSI) | |
("m_front_right_tyre_pressure", ctypes.c_float),# Front right tyre pressure (PSI) | |
("m_ballast", ctypes.c_uint8), # Ballast | |
("m_fuel_load", ctypes.c_float), # Fuel load | |
] | |
class PacketCarSetupData(Packet): | |
_fields_ = [ | |
("m_header", PacketHeader), # Header | |
("m_car_setups", CarSetupData * 22), # Data for all cars on track | |
("m_next_front_wing_value", ctypes.c_float) # Value of front wing after next pit stop - player only | |
] | |
class CarTelemetryData(Packet): | |
_fields_ = [ | |
("m_speed", ctypes.c_uint16), # Speed of car in kilometres per hour | |
("m_throttle", ctypes.c_float), # Amount of throttle applied (0.0 to 1.0) | |
("m_steer", ctypes.c_float), # Steering (-1.0 (full lock left) to 1.0 (full lock right)) | |
("m_brake", ctypes.c_float), # Amount of brake applied (0.0 to 1.0) | |
("m_clutch", ctypes.c_uint8), # Amount of clutch applied (0 to 100) | |
("m_gear", ctypes.c_int8), # Gear selected (1-8, N=0, R=-1) | |
("m_engine_rpm", ctypes.c_uint16), # Engine RPM | |
("m_drs", ctypes.c_uint8), # 0 = off, 1 = on | |
("m_rev_lights_percent", ctypes.c_uint8), # Rev lights indicator (percentage) | |
("m_rev_lights_bit_value", ctypes.c_uint16), # Rev lights (bit 0 = leftmost LED, bit 14 = rightmost LED) | |
("m_brakes_temperature", ctypes.c_uint16 * 4), # Brakes temperature (celsius) | |
("m_tyres_surface_temperature", ctypes.c_uint8 * 4), # Tyres surface temperature (celsius) | |
("m_tyres_inner_temperature", ctypes.c_uint8 * 4), # Tyres inner temperature (celsius) | |
("m_engine_temperature", ctypes.c_uint16), # Engine temperature (celsius) | |
("m_tyres_pressure", ctypes.c_float * 4), # Tyres pressure (PSI) | |
("m_surface_type", ctypes.c_uint8 * 4), # Driving surface, see appendices | |
] | |
class PacketCarTelemetryData(Packet): | |
_fields_ = [ | |
("m_header", PacketHeader), # Header | |
("m_car_telemetry_data", CarTelemetryData * 22), # Data for all cars on track | |
("m_mfd_panel_index", ctypes.c_uint8), # Index of MFD panel open - 255 = MFD closed | |
("m_mfd_panel_index_secondary_player", ctypes.c_uint8), # See above for secondary player | |
("m_suggested_gear", ctypes.c_int8), # Suggested gear for the player (1-8), 0 if no gear suggested | |
] | |
class CarStatusData(Packet): | |
_fields_ = [ | |
("m_traction_control", ctypes.c_uint8), # Traction control - 0 = off, 1 = medium, 2 = full | |
("m_anti_lock_brakes", ctypes.c_uint8), # 0 (off) - 1 (on) | |
("m_fuel_mix", ctypes.c_uint8), # Fuel mix - 0 = lean, 1 = standard, 2 = rich, 3 = max | |
("m_front_brake_bias", ctypes.c_uint8), # Front brake bias (percentage) | |
("m_pit_limiter_status", ctypes.c_uint8), # Pit limiter status - 0 = off, 1 = on | |
("m_fuel_in_tank", ctypes.c_float), # Current fuel mass | |
("m_fuel_capacity", ctypes.c_float), # Fuel capacity | |
("m_fuel_remaining_laps", ctypes.c_float), # Fuel remaining in terms of laps (value on MFD) | |
("m_max_rpm", ctypes.c_uint16), # Cars max RPM, point of rev limiter | |
("m_idle_rpm", ctypes.c_uint16), # Cars idle RPM | |
("m_max_gears", ctypes.c_uint8), # Maximum number of gears | |
("m_drs_allowed", ctypes.c_uint8), # 0 = not allowed, 1 = allowed | |
("m_drs_activation_distance", ctypes.c_uint16), # 0 = DRS not available, non-zero - DRS will be available in [X] metres | |
("m_actual_tyre_compound", ctypes.c_uint8), # F1 Modern - 16 = C5, 17 = C4, 18 = C3, 19 = C2, 20 = C1, 21 = C0, 7 = inter, 8 = wet. F1 Classic - 9 = dry, 10 = wet. F2 – 11 = super soft, 12 = soft, 13 = medium, 14 = hard, 15 = wet | |
("m_visual_tyre_compound", ctypes.c_uint8), # F1 visual (can be different from actual compound), 16 = soft, 17 = medium, 18 = hard, 7 = inter, 8 = wet. F1 Classic – same as above. F2 '19, 15 = wet, 19 – super soft, 20 = soft, 21 = medium , 22 = hard | |
("m_tyres_age_laps", ctypes.c_uint8), # Age in laps of the current set of tyres | |
("m_vehicle_fia_flags", ctypes.c_int8), # -1 = invalid/unknown, 0 = none, 1 = green, 2 = blue, 3 = yellow | |
("m_engine_power_ice", ctypes.c_float), # Engine power output of ICE (W) | |
("m_engine_power_mguk", ctypes.c_float), # Engine power output of MGU-K (W) | |
("m_ers_store_energy", ctypes.c_float), # ERS energy store in Joules | |
("m_ers_deploy_mode", ctypes.c_uint8), # ERS deployment mode, 0 = none, 1 = medium, 2 = hotlap, 3 = overtake | |
("m_ers_harvested_this_lap_mguk", ctypes.c_float), # ERS energy harvested this lap by MGU-K | |
("m_ers_harvested_this_lap_mguh", ctypes.c_float), # ERS energy harvested this lap by MGU-H | |
("m_ers_deployed_this_lap", ctypes.c_float), # ERS energy deployed this lap | |
("m_network_paused", ctypes.c_uint8), # Whether the car is paused in a network game | |
] | |
class PacketCarStatusData(Packet): | |
_fields_ = [ | |
("m_header", PacketHeader), # Header | |
("m_car_status_data", CarStatusData * 22), # Data for all cars on track | |
] | |
class FinalClassificationData(Packet): | |
_fields_ = [ | |
("m_position", ctypes.c_uint8), # Finishing position | |
("m_num_laps", ctypes.c_uint8), # Number of laps completed | |
("m_grid_position", ctypes.c_uint8), # Grid position of the car | |
("m_points", ctypes.c_uint8), # Number of points scored | |
("m_num_pit_stops", ctypes.c_uint8), # Number of pit stops made | |
("m_result_status", ctypes.c_uint8), # Result status - 0 = invalid, 1 = inactive, 2 = active, | |
# 3 = finished, 4 = didnotfinish, 5 = disqualified, | |
# 6 = not classified, 7 = retired | |
("m_best_lap_time_in_ms", ctypes.c_uint32), # Best lap time of the session in milliseconds | |
("m_total_race_time", ctypes.c_double), # Total race time in seconds without penalties | |
("m_penalties_time", ctypes.c_uint8), # Total penalties accumulated in seconds | |
("m_num_penalties", ctypes.c_uint8), # Number of penalties applied to this driver | |
("m_num_tyre_stints", ctypes.c_uint8), # Number of tyres stints up to maximum | |
("m_tyre_stints_actual", ctypes.c_uint8 * 8), # Actual tyres used by this driver | |
("m_tyre_stints_visual", ctypes.c_uint8 * 8), # Visual tyres used by this driver | |
("m_tyre_stints_end_laps", ctypes.c_uint8 * 8), # The lap number stints end on | |
] | |
class PacketFinalClassificationData(Packet): | |
_fields_ = [ | |
("m_header", PacketHeader), # Header | |
("m_num_cars", ctypes.c_uint8), # Number of cars in the final classification | |
("m_classification_data", FinalClassificationData * 22), # Data for all cars | |
] | |
class LobbyInfoData(Packet): | |
_fields_ = [ | |
("m_ai_controlled", ctypes.c_uint8), # Whether the vehicle is AI (1) or Human (0) controlled | |
("m_team_id", ctypes.c_uint8), # Team id - see appendix (255 if no team currently selected) | |
("m_nationality", ctypes.c_uint8), # Nationality of the driver | |
("m_platform", ctypes.c_uint8), # 1 = Steam, 3 = PlayStation, 4 = Xbox, 6 = Origin, 255 = unknown | |
("m_name", ctypes.c_char * 48), # Name of participant in UTF-8 format – null terminated | |
("m_car_number", ctypes.c_uint8), # Car number of the player | |
("m_your_telemetry", ctypes.c_uint8), # The player's UDP setting, 0 = restricted, 1 = public | |
("m_show_online_names", ctypes.c_uint8), # The player's show online names setting, 0 = off, 1 = on | |
("m_tech_level", ctypes.c_uint16), # F1 World tech level | |
("m_ready_status", ctypes.c_uint8), # 0 = not ready, 1 = ready, 2 = spectating | |
] | |
class PacketLobbyInfoData(Packet): | |
_fields_ = [ | |
("m_header", PacketHeader), # Header | |
("m_num_players", ctypes.c_uint8), # Number of players in the lobby data | |
("m_lobby_players", LobbyInfoData * 22), # Array of lobby players data | |
] | |
class CarDamageData(Packet): | |
_fields_ = [ | |
("m_tyres_wear", ctypes.c_float * 4), # Tyre wear (percentage) | |
("m_tyres_damage", ctypes.c_uint8 * 4), # Tyre damage (percentage) | |
("m_brakes_damage", ctypes.c_uint8 * 4), # Brakes damage (percentage) | |
("m_front_left_wing_damage", ctypes.c_uint8), # Front left wing damage (percentage) | |
("m_front_right_wing_damage", ctypes.c_uint8), # Front right wing damage (percentage) | |
("m_rear_wing_damage", ctypes.c_uint8), # Rear wing damage (percentage) | |
("m_floor_damage", ctypes.c_uint8), # Floor damage (percentage) | |
("m_diffuser_damage", ctypes.c_uint8), # Diffuser damage (percentage) | |
("m_sidepod_damage", ctypes.c_uint8), # Sidepod damage (percentage) | |
("m_drs_fault", ctypes.c_uint8), # Indicator for DRS fault, 0 = OK, 1 = fault | |
("m_ers_fault", ctypes.c_uint8), # Indicator for ERS fault, 0 = OK, 1 = fault | |
("m_gear_box_damage", ctypes.c_uint8), # Gear box damage (percentage) | |
("m_engine_damage", ctypes.c_uint8), # Engine damage (percentage) | |
("m_engine_mguh_wear", ctypes.c_uint8), # Engine wear MGU-H (percentage) | |
("m_engine_es_wear", ctypes.c_uint8), # Engine wear ES (percentage) | |
("m_engine_ce_wear", ctypes.c_uint8), # Engine wear CE (percentage) | |
("m_engine_ice_wear", ctypes.c_uint8), # Engine wear ICE (percentage) | |
("m_engine_mguk_wear", ctypes.c_uint8), # Engine wear MGU-K (percentage) | |
("m_engine_tc_wear", ctypes.c_uint8), # Engine wear TC (percentage) | |
("m_engine_blown", ctypes.c_uint8), # Engine blown, 0 = OK, 1 = fault | |
("m_engine_seized", ctypes.c_uint8), # Engine seized, 0 = OK, 1 = fault | |
] | |
class PacketCarDamageData(Packet): | |
_fields_ = [ | |
("m_header", PacketHeader), # Header | |
("m_car_damage_data", CarDamageData * 22), # Data for all cars on track | |
] | |
class LapHistoryData(Packet): | |
_fields_ = [ | |
("m_lap_time_in_ms", ctypes.c_uint32), # Lap time in milliseconds | |
("m_sector1_time_ms_part", ctypes.c_uint16), # Sector 1 milliseconds part | |
("m_sector1_time_minutes_part", ctypes.c_uint8), # Sector 1 whole minute part | |
("m_sector2_time_ms_part", ctypes.c_uint16), # Sector 2 time milliseconds part | |
("m_sector2_time_minutes_part", ctypes.c_uint8), # Sector 2 whole minute part | |
("m_sector3_time_ms_part", ctypes.c_uint16), # Sector 3 time milliseconds part | |
("m_sector3_time_minutes_part", ctypes.c_uint8), # Sector 3 whole minute part | |
("m_lap_valid_bit_flags", ctypes.c_uint8) # 0x01 bit set-lap valid, 0x02 bit set-sector 1 valid, | |
# 0x04 bit set-sector 2 valid, 0x08 bit set-sector 3 valid | |
] | |
class TyreStintHistoryData(Packet): | |
_fields_ = [ | |
("m_end_lap", ctypes.c_uint8), # Lap the tyre usage ends on (255 of current tyre) | |
("m_tyre_actual_compound", ctypes.c_uint8), # Actual tyres used by this driver | |
("m_tyre_visual_compound", ctypes.c_uint8) # Visual tyres used by this driver | |
] | |
class PacketSessionHistoryData(Packet): | |
_fields_ = [ | |
("m_header", PacketHeader), # Header | |
("m_car_idx", ctypes.c_uint8), # Index of the car this lap data relates to | |
("m_num_laps", ctypes.c_uint8), # Num laps in the data (including current partial lap) | |
("m_num_tyre_stints", ctypes.c_uint8), # Number of tyre stints in the data | |
("m_best_lap_time_lap_num", ctypes.c_uint8), # Lap the best lap time was achieved on | |
("m_best_sector1_lap_num", ctypes.c_uint8), # Lap the best Sector 1 time was achieved on | |
("m_best_sector2_lap_num", ctypes.c_uint8), # Lap the best Sector 2 time was achieved on | |
("m_best_sector3_lap_num", ctypes.c_uint8), # Lap the best Sector 3 time was achieved on | |
("m_lap_history_data", LapHistoryData * 100), # 100 laps of data max (0 = first lap, 1 = second lap, ...) | |
("m_tyre_stints_history_data", TyreStintHistoryData * 8) # 8 tyre stints max | |
] | |
class TyreSetData(Packet): | |
_fields_ = [ | |
("m_actual_tyre_compound", ctypes.c_uint8), # Actual tyre compound used | |
("m_visual_tyre_compound", ctypes.c_uint8), # Visual tyre compound used | |
("m_wear", ctypes.c_uint8), # Tyre wear (percentage) | |
("m_available", ctypes.c_uint8), # Whether this set is currently available | |
("m_recommended_session", ctypes.c_uint8), # Recommended session for tyre set, see appendix | |
("m_life_span", ctypes.c_uint8), # Laps left in this tyre set | |
("m_usable_life", ctypes.c_uint8), # Max number of laps recommended for this compound | |
("m_lap_delta_time", ctypes.c_int16), # Lap delta time in milliseconds compared to fitted set | |
("m_fitted", ctypes.c_uint8), # Whether the set is fitted or not | |
] | |
class PacketTyreSetsData(Packet): | |
_fields_ = [ | |
("m_header", PacketHeader), # Header | |
("m_car_idx", ctypes.c_uint8), # Index of the car this data relates to | |
("m_tyre_set_data", TyreSetData * 20), # 13 (dry) + 7 (wet) | |
("m_fitted_idx", ctypes.c_uint8), # Index into array of fitted tyre | |
] | |
class PacketMotionExData(Packet): | |
_fields_ = [ | |
("m_header", PacketHeader), # Header | |
("m_suspension_position", ctypes.c_float * 4), # Note: All wheel arrays have the following order: | |
("m_suspension_velocity", ctypes.c_float * 4), # RL, RR, FL, FR | |
("m_suspension_acceleration", ctypes.c_float * 4), # RL, RR, FL, FR | |
("m_wheel_speed", ctypes.c_float * 4), # Speed of each wheel | |
("m_wheel_slip_ratio", ctypes.c_float * 4), # Slip ratio for each wheel | |
("m_wheel_slip_angle", ctypes.c_float * 4), # Slip angles for each wheel | |
("m_wheel_lat_force", ctypes.c_float * 4), # Lateral forces for each wheel | |
("m_wheel_long_force", ctypes.c_float * 4), # Longitudinal forces for each wheel | |
("m_height_of_cog_above_ground", ctypes.c_float), # Height of centre of gravity above ground | |
("m_local_velocity_x", ctypes.c_float), # Velocity in local space – metres/s | |
("m_local_velocity_y", ctypes.c_float), # Velocity in local space | |
("m_local_velocity_z", ctypes.c_float), # Velocity in local space | |
("m_angular_velocity_x", ctypes.c_float), # Angular velocity x-component – radians/s | |
("m_angular_velocity_y", ctypes.c_float), # Angular velocity y-component | |
("m_angular_velocity_z", ctypes.c_float), # Angular velocity z-component | |
("m_angular_acceleration_x", ctypes.c_float), # Angular acceleration x-component – radians/s/s | |
("m_angular_acceleration_y", ctypes.c_float), # Angular acceleration y-component | |
("m_angular_acceleration_z", ctypes.c_float), # Angular acceleration z-component | |
("m_front_wheels_angle", ctypes.c_float), # Current front wheels angle in radians | |
("m_wheel_vert_force", ctypes.c_float * 4), # Vertical forces for each wheel | |
("m_front_aero_height", ctypes.c_float), # Front plank edge height above road surface | |
("m_rear_aero_height", ctypes.c_float), # Rear plank edge height above road surface | |
("m_front_roll_angle", ctypes.c_float), # Roll angle of the front suspension | |
("m_rear_roll_angle", ctypes.c_float), # Roll angle of the rear suspension | |
("m_chassis_yaw", ctypes.c_float) # Yaw angle of the chassis relative to the direction of motion - radians | |
] | |
class TimeTrialDataSet(Packet): | |
_fields_ = [ | |
("m_car_idx", ctypes.c_uint8), # Index of the car this data relates to | |
("m_team_id", ctypes.c_uint8), # Team id - see appendix | |
("m_lap_time_in_ms", ctypes.c_uint32), # Lap time in milliseconds | |
("m_sector1_time_in_ms", ctypes.c_uint32), # Sector 1 time in milliseconds | |
("m_sector2_time_in_ms", ctypes.c_uint32), # Sector 2 time in milliseconds | |
("m_sector3_time_in_ms", ctypes.c_uint32), # Sector 3 time in milliseconds | |
("m_traction_control", ctypes.c_uint8), # 0 = off, 1 = medium, 2 = full | |
("m_gearbox_assist", ctypes.c_uint8), # 1 = manual, 2 = manual & suggested gear, 3 = auto | |
("m_anti_lock_brakes", ctypes.c_uint8), # 0 (off) - 1 (on) | |
("m_equal_car_performance", ctypes.c_uint8), # 0 = Realistic, 1 = Equal | |
("m_custom_setup", ctypes.c_uint8), # 0 = No, 1 = Yes | |
("m_valid", ctypes.c_uint8), # 0 = invalid, 1 = valid | |
] | |
class PacketTimeTrialData(Packet): | |
_fields_ = [ | |
("m_header", PacketHeader), # Header | |
("m_player_session_best_data_set", TimeTrialDataSet), # Player session best data set | |
("m_personal_best_data_set", TimeTrialDataSet), # Personal best data set | |
("m_rival_data_set", TimeTrialDataSet), # Rival data set | |
] | |
class EventStringCode(Enum): | |
SSTA = b'SSTA' # Session Started | |
SEND = b'SEND' # Session Ended | |
FTLP = b'FTLP' # Fastest Lap | |
RTMT = b'RTMT' # Retirement | |
DRSE = b'DRSE' # DRS enabled | |
DRSD = b'DRSD' # DRS disabled | |
TMPT = b'TMPT' # Team mate in pits | |
CHQF = b'CHQF' # Chequered flag | |
RCWN = b'RCWN' # Race Winner | |
PENA = b'PENA' # Penalty Issued | |
SPTP = b'SPTP' # Speed Trap Triggered | |
STLG = b'STLG' # Start lights | |
DTSV = b'DTSV' # Drive through served | |
SGSV = b'SGSV' # Stop go served | |
FLBK = b'FLBK' # Flashback | |
BUTN = b'BUTN' # Button status | |
OVTK = b'OVTK' # Overtake | |
SCDB = b'SCDB' # Safety Car | |
COLS = b'COLS' # Collision | |
class F1TelemetryClient: | |
# Map packet IDs to their corresponding packet classes | |
PACKET_TYPES = { | |
PacketID.MOTION: PacketMotionData, | |
PacketID.SESSION: PacketSessionData, | |
PacketID.LAP_DATA: PacketLapData, | |
PacketID.EVENT: PacketEventData, | |
PacketID.PARTICIPANTS: PacketParticipantsData, | |
PacketID.CAR_SETUPS: PacketCarSetupData, | |
PacketID.CAR_TELEMETRY: PacketCarTelemetryData, | |
PacketID.CAR_STATUS: PacketCarStatusData, | |
PacketID.FINAL_CLASSIFICATION: PacketFinalClassificationData, | |
PacketID.LOBBY_INFO: PacketLobbyInfoData, | |
PacketID.CAR_DAMAGE: PacketCarDamageData, | |
PacketID.SESSION_HISTORY: PacketSessionHistoryData, | |
PacketID.TYRE_SETS: PacketTyreSetsData, | |
PacketID.MOTION_EX: PacketMotionExData, | |
PacketID.TIME_TRIAL: PacketTimeTrialData | |
} | |
def __init__(self, ip="0.0.0.0", port=20777): | |
self.ip = ip | |
self.port = port | |
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
self.socket.bind((self.ip, self.port)) | |
self.socket.setblocking(False) | |
self.player_car_index = None | |
def receive_packet(self): | |
""" | |
Try to receive a UDP packet and parse it into the appropriate packet structure. | |
Returns the parsed packet object or None if no data is available. | |
""" | |
try: | |
data, addr = self.socket.recvfrom(2048) | |
# First, parse just the header to identify the packet type | |
header = PacketHeader.from_buffer_copy(data) | |
# Track player car index | |
if self.player_car_index is None: | |
self.player_car_index = header.m_player_car_index | |
# If we have a packet class for this packet type, parse and return it | |
packet_id = header.m_packet_id | |
if packet_id in self.PACKET_TYPES: | |
# Use the appropriate packet class to parse the data | |
packet_class = self.PACKET_TYPES[packet_id] | |
return packet_class.from_buffer_copy(data) | |
return None | |
except BlockingIOError: | |
# No data available, return None | |
return None | |
except Exception as e: | |
print(f"Error receiving packet: {e}") | |
return None | |
def run(self, callback=None): | |
"""Main loop to continually receive packets""" | |
print(f"Listening for F1 24 telemetry on {self.ip}:{self.port}") | |
try: | |
while True: | |
packet = self.receive_packet() | |
if packet and callback: | |
callback(packet) | |
# Small sleep to prevent maxing out CPU | |
time.sleep(0.001) | |
except KeyboardInterrupt: | |
print("Shutting down telemetry client...") | |
finally: | |
self.socket.close() | |
if __name__ == "__main__": | |
# Example usage - just print data from every packet type | |
def raw_packet_handler(packet): | |
if isinstance(packet, PacketMotionData): | |
# print(f"Received MOTION packet, frame: {packet.m_header.m_frame_identifier}, packet: {packet.to_dict()}") | |
# Access to raw packet data without transformation | |
player_index = packet.m_header.m_player_car_index | |
player_car = packet.m_car_motion_data[player_index] | |
# print(f"player_car: {player_car.m_g_force_lateral}") | |
elif isinstance(packet, PacketSessionData): | |
# print(f"Received SESSION packet, frame: {packet.m_header.m_frame_identifier}, packet: {packet.to_dict()}") | |
m_speed_units_lead_player = packet.m_speed_units_lead_player | |
# print(f"m_speed_units_lead_player: {m_speed_units_lead_player}") | |
elif isinstance(packet, PacketLapData): | |
# print(f"Received LAP_DATA packet, frame: {packet.m_header.m_frame_identifier}, packet: {packet.to_dict()}") | |
player_index = packet.m_header.m_player_car_index | |
m_lap_data = packet.m_lap_data[player_index] | |
# print(f"m_current_lap_time_in_ms: {m_lap_data.m_current_lap_time_in_ms}") | |
elif isinstance(packet, PacketEventData): | |
# print(f"Received EVENT packet, frame: {packet.m_header.m_frame_identifier}, packet: {packet.to_dict()}") | |
event_code = bytes(packet.m_event_string_code) | |
if event_code == EventStringCode.PENA.value: | |
penalty = packet.m_event_details.penalty # see EventDataDetails class to check variable depending on event_code | |
# print(f"Penalty for vehicle {penalty.vehicle_idx}, type: {penalty.penalty_type}") | |
elif isinstance(packet, PacketParticipantsData): | |
# print(f"Received PARTICIPANTS packet, frame: {packet.m_header.m_frame_identifier}, packet: {packet.to_dict()}") | |
player_index = packet.m_header.m_player_car_index | |
m_name = packet.m_participants[player_index].m_name | |
# print(f"m_name: {m_name}") | |
elif isinstance(packet, PacketCarSetupData): | |
# print(f"Received CAR_SETUPS packet, frame: {packet.m_header.m_frame_identifier}, packet: {packet.to_dict()}") | |
player_index = packet.m_header.m_player_car_index | |
m_brake_bias = packet.m_car_setups[player_index].m_brake_bias | |
# print(f"m_brake_bias: {m_brake_bias}") | |
elif isinstance(packet, PacketCarTelemetryData): | |
# print(f"Received CAR_TELEMETRY packet, frame: {packet.m_header.m_frame_identifier}, packet: {packet.to_dict()}") | |
player_index = packet.m_header.m_player_car_index | |
m_gear = packet.m_car_telemetry_data[player_index].m_gear | |
# print(f"m_gear: {m_gear}") | |
elif isinstance(packet, PacketCarStatusData): | |
# print(f"Received CAR_STATUS packet, frame: {packet.m_header.m_frame_identifier}, packet: {packet.to_dict()}") | |
player_index = packet.m_header.m_player_car_index | |
m_fuel_remaining_laps = packet.m_car_status_data[player_index].m_fuel_remaining_laps | |
# print(f"m_fuel_remaining_laps: {m_fuel_remaining_laps}") | |
elif isinstance(packet, PacketFinalClassificationData): | |
# print(f"Received FINAL_CLASSIFICATION packet, frame: {packet.m_header.m_frame_identifier}, packet: {packet.to_dict()}") | |
player_index = packet.m_header.m_player_car_index | |
m_result_status = packet.m_classification_data[player_index].m_result_status | |
# print(f"m_result_status: {m_result_status}") | |
elif isinstance(packet, PacketLobbyInfoData): | |
# print(f"Received LOBBY_INFO packet, frame: {packet.m_header.m_frame_identifier}, packet: {packet.to_dict()}") | |
player_index = packet.m_header.m_player_car_index | |
m_name = packet.m_lobby_players[player_index].m_name | |
# print(f"m_name: {m_name}") | |
elif isinstance(packet, PacketCarDamageData): | |
# print(f"Received CAR_DAMAGE packet, frame: {packet.m_header.m_frame_identifier}, packet: {packet.to_dict()}") | |
player_index = packet.m_header.m_player_car_index | |
m_tyres_wear = packet.m_car_damage_data[player_index].m_tyres_wear[0] | |
# print(f"m_tyres_wear: {m_tyres_wear}") | |
elif isinstance(packet, PacketSessionHistoryData): | |
# print(f"Received SESSION_HISTORY packet, frame: {packet.m_header.m_frame_identifier}, packet: {packet.to_dict()}") | |
player_index = packet.m_header.m_player_car_index | |
if packet.m_car_idx == player_index: | |
m_sector1_time_ms_part = packet.m_lap_history_data[0].m_sector1_time_ms_part | |
# print(f"m_sector1_time_ms_part: {m_sector1_time_ms_part}") | |
elif isinstance(packet, PacketTyreSetsData): | |
# print(f"Received TYRE_SETS packet, frame: {packet.m_header.m_frame_identifier}, packet: {packet.to_dict()}") | |
player_index = packet.m_header.m_player_car_index | |
if packet.m_car_idx == player_index: | |
m_wear = packet.m_tyre_set_data[0].m_wear | |
# print(f"m_wear: {m_wear}") | |
elif isinstance(packet, PacketMotionExData): | |
# print(f"Received MOTION_EX packet, frame: {packet.m_header.m_frame_identifier}, packet: {packet.to_dict()}") | |
m_wheel_speed = packet.m_wheel_speed[0] | |
# print(f"m_wheel_speed: {m_wheel_speed}") | |
elif isinstance(packet, PacketTimeTrialData): | |
# print(f"Received TIME_TRIAL packet, frame: {packet.m_header.m_frame_identifier}, packet: {packet.to_dict()}") | |
m_lap_time_in_ms = packet.m_personal_best_data_set.m_lap_time_in_ms | |
# print(f"m_lap_time_in_ms: {m_lap_time_in_ms}") | |
else: | |
print(f"Received packet type: {packet.m_header.m_packet_id}") | |
# Create a client and register packet types | |
client = F1TelemetryClient() | |
# Run client with raw packet handler | |
client.run(callback=raw_packet_handler) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment