Skip to main content

tf_demo_parser/demo/message/
serverinfo.rs

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