1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
//! Header prefixing packets from modern F1 games use crate::types::VehicleIndex; use derive_new::new; use getset::{CopyGetters, Getters}; use std::fmt; use std::fmt::Display; use std::time::Duration; /// Supported API specifications /// /// The modern F1 games have their own API specifications, each an evolution of the previous one. /// Since the data published by each game is unique in one way or another, support for additional /// API specs has to be implemented manually. #[derive(Debug, PartialEq, Copy, Clone, Eq, Ord, PartialOrd, Hash)] pub enum ApiSpec { Nineteen, } /// Packets sent by F1 games /// /// The modern F1 games have divided their telemetry output into multiple packets, which can be sent /// at different intervals based on how quickly their data changes. #[derive(Debug, PartialEq, Copy, Clone, Eq, Ord, PartialOrd, Hash)] pub enum PacketType { Event, Lap, Motion, Participants, Session, Setup, Status, Telemetry, } /// Version number of the game /// /// The modern F1 games include their version number in the packet header. The games are versioned /// using the scheme `MAJOR.MINOR`. /// /// TODO Test that partial order works correctly with version numbers #[derive( new, Debug, Getters, CopyGetters, PartialEq, Copy, Clone, Eq, Ord, PartialOrd, Hash, Default, )] pub struct GameVersion { /// Returns the major version of the game. #[getset(get_copy = "pub")] major: u8, /// Returns the minor version of the game. #[getset(get_copy = "pub")] minor: u8, } impl Display for GameVersion { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}.{}", self.major, self.minor) } } /// Header prefixing each packet /// /// The modern F1 games use versioned API specifications. Each packet is prefixed with a header that /// declares which version of the specification the packet adheres to. This information is required /// to decode the packet correctly. Because it is only relevant for decoding the packet, the packet /// format, type, and version from the specifications are not republished. /// /// The header also contains information about the session the packet belongs to, and about the time /// the packet was created. /// /// TODO Verify that the session tie can be represented as a duration #[derive(new, Debug, Getters, CopyGetters, PartialEq, Copy, Clone, Eq, Ord, PartialOrd, Hash)] pub struct Header { /// Returns the API specification that was used to decode the packet. #[getset(get_copy = "pub")] api_spec: ApiSpec, /// Returns the version of the game. #[getset(get = "pub")] game_version: Option<GameVersion>, /// Returns the type of the packet. /// /// The packet type is only required to determine how to decode the packet. After decoding it, /// the packet type is represented by Rust's type system. #[getset(get_copy = "pub")] packet_type: PacketType, /// Returns the unique session UID. #[getset(get_copy = "pub")] session_uid: u64, /// Returns the session time at the time the packet was sent. #[getset(get = "pub")] session_time: Duration, /// Returns the frame identifier at the time the packet was sent. #[getset(get_copy = "pub")] frame_identifier: u32, /// Returns the player's car index. /// /// The setups and status of cars are published as arrays. This field indicates which position /// in these arrays the player's car has. #[getset(get_copy = "pub")] player_car_index: VehicleIndex, } impl Display for Header { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let game_version = match self.game_version { Some(version) => format!("{}", version), None => String::from("None"), }; write!( f, "Header {{ game_version: {}, session: {}, time: {}s, frame: {}, player_car_index: {} }}", game_version, self.session_uid, self.session_time.as_secs(), self.frame_identifier, self.player_car_index ) } }