tf_demo_parser/demo/message/
serverinfo.rs

1use crate::demo::parser::{Encode, ParseBitSkip};
2use crate::{Parse, ParserState, Result, Stream};
3use bitbuffer::{BitRead, BitWrite, BitWriteStream, LittleEndian};
4use serde::{Deserialize, Serialize};
5
6#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
7#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
8pub struct ServerInfoMessage {
9    pub version: u16,
10    pub server_count: u32,
11    pub stv: bool,
12    pub dedicated: bool,
13    pub max_crc: u32,
14    pub max_classes: u16,
15    pub map_hash: [u8; 16],
16    pub player_slot: u8,
17    pub max_player_count: u8,
18    pub interval_per_tick: f32,
19    pub platform: String,
20    pub game: String,
21    pub map: String,
22    pub skybox: String,
23    pub server_name: String,
24    pub replay: bool,
25}
26
27impl<'a> Parse<'a> for ServerInfoMessage {
28    fn parse(stream: &mut Stream<'a>, state: &ParserState) -> Result<Self> {
29        let part1 = ServerInfoMessagePart1::read(stream)?;
30        let map_hash = if state.protocol_version > 17 {
31            <[u8; 16]>::read(stream)?
32        } else {
33            let mut hash = [0; 16];
34            let crc = u32::read(stream)?;
35            hash[0..4].copy_from_slice(&crc.to_le_bytes());
36            hash
37        };
38        let part2 = ServerInfoMessagePart2::read(stream)?;
39        let replay = if state.protocol_version > 15 {
40            bool::read(stream)?
41        } else {
42            false
43        };
44        Ok(ServerInfoMessage {
45            version: part1.version,
46            server_count: part1.server_count,
47            stv: part1.stv,
48            dedicated: part1.dedicated,
49            max_crc: part1.max_crc,
50            max_classes: part1.max_classes,
51            map_hash,
52            player_slot: part2.player_slot,
53            max_player_count: part2.max_player_count,
54            interval_per_tick: part2.interval_per_tick,
55            platform: part2.platform,
56            game: part2.game,
57            map: part2.map,
58            skybox: part2.skybox,
59            server_name: part2.server_name,
60            replay,
61        })
62    }
63}
64
65impl<'a> ParseBitSkip<'a> for ServerInfoMessage {
66    fn parse_skip(stream: &mut Stream<'a>, state: &ParserState) -> Result<()> {
67        let version_dependent_size = match state.protocol_version {
68            0..=15 => 4 * 8,      // only the 4 byte crc
69            16..=17 => 4 * 8 + 1, // adds the 1 bit replay flag
70            18.. => 16 * 8 + 1,   // replaces 4 byte crc with an 16 byte hash
71        };
72        let size = <ServerInfoMessagePart1 as BitRead<LittleEndian>>::bit_size()
73            .unwrap_or_default()
74            + <ServerInfoMessagePart2 as BitRead<LittleEndian>>::bit_size().unwrap_or_default()
75            + version_dependent_size;
76        stream.skip_bits(size)?;
77        Ok(())
78    }
79}
80
81impl Encode for ServerInfoMessage {
82    fn encode(&self, stream: &mut BitWriteStream<LittleEndian>, state: &ParserState) -> Result<()> {
83        let part1 = ServerInfoMessagePart1 {
84            version: self.version,
85            server_count: self.server_count,
86            stv: self.stv,
87            dedicated: self.dedicated,
88            max_crc: self.max_crc,
89            max_classes: self.max_classes,
90        };
91        part1.write(stream)?;
92        if state.protocol_version > 17 {
93            self.map_hash.write(stream)?;
94        } else {
95            let crc = u32::from_le_bytes([
96                self.map_hash[0],
97                self.map_hash[1],
98                self.map_hash[2],
99                self.map_hash[3],
100            ]);
101            crc.write(stream)?;
102        };
103        let part2 = ServerInfoMessagePart2 {
104            player_slot: self.player_slot,
105            max_player_count: self.max_player_count,
106            interval_per_tick: self.interval_per_tick,
107            platform: self.platform.clone(),
108            game: self.game.clone(),
109            map: self.map.clone(),
110            skybox: self.skybox.clone(),
111            server_name: self.server_name.clone(),
112        };
113        part2.write(stream)?;
114        if state.protocol_version > 15 {
115            self.replay.write(stream)?;
116        }
117        Ok(())
118    }
119}
120
121#[derive(BitRead, BitWrite)]
122pub struct ServerInfoMessagePart1 {
123    pub version: u16,
124    pub server_count: u32,
125    pub stv: bool,
126    pub dedicated: bool,
127    pub max_crc: u32,
128    pub max_classes: u16,
129}
130
131#[derive(BitRead, BitWrite)]
132pub struct ServerInfoMessagePart2 {
133    pub player_slot: u8,
134    pub max_player_count: u8,
135    pub interval_per_tick: f32,
136    #[size = 1]
137    pub platform: String,
138    pub game: String,
139    pub map: String,
140    pub skybox: String,
141    pub server_name: String,
142}