Skip to content
This repository was archived by the owner on May 21, 2025. It is now read-only.

Commit 4bd97e4

Browse files
committed
handle older serverinfo messages
1 parent 442c496 commit 4bd97e4

3 files changed

Lines changed: 153 additions & 32 deletions

File tree

src/demo/message/generated.rs

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -53,28 +53,6 @@ pub struct PrintMessage {
5353
pub value: MaybeUtf8String,
5454
}
5555

56-
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
57-
#[derive(BitRead, BitWrite, Debug, PartialEq, Serialize, Deserialize, Clone)]
58-
pub struct ServerInfoMessage {
59-
pub version: u16,
60-
pub server_count: u32,
61-
pub stv: bool,
62-
pub dedicated: bool,
63-
pub max_crc: u32,
64-
pub max_classes: u16,
65-
pub map_hash: [u8; 16],
66-
pub player_slot: u8,
67-
pub max_player_count: u8,
68-
pub interval_per_tick: f32,
69-
#[size = 1]
70-
pub platform: String,
71-
pub game: String,
72-
pub map: String,
73-
pub skybox: String,
74-
pub server_name: String,
75-
pub replay: bool,
76-
}
77-
7856
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
7957
#[derive(BitRead, BitWrite, Debug, PartialEq, Serialize, Deserialize, Clone)]
8058
pub struct SetPauseMessage {

src/demo/message/mod.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
pub use generated::*;
22

3-
use crate::demo::message::bspdecal::*;
4-
use crate::demo::message::classinfo::*;
5-
use crate::demo::message::gameevent::*;
6-
use crate::demo::message::packetentities::*;
7-
use crate::demo::message::setconvar::*;
8-
use crate::demo::message::stringtable::*;
9-
use crate::demo::message::tempentities::*;
10-
use crate::demo::message::usermessage::*;
11-
use crate::demo::message::voice::*;
12-
use crate::demo::message::prefetch::*;
3+
pub use crate::demo::message::bspdecal::*;
4+
pub use crate::demo::message::classinfo::*;
5+
pub use crate::demo::message::gameevent::*;
6+
pub use crate::demo::message::packetentities::*;
7+
pub use crate::demo::message::setconvar::*;
8+
pub use crate::demo::message::stringtable::*;
9+
pub use crate::demo::message::tempentities::*;
10+
pub use crate::demo::message::usermessage::*;
11+
pub use crate::demo::message::voice::*;
12+
pub use crate::demo::message::prefetch::*;
13+
pub use crate::demo::message::serverinfo::*;
1314
use crate::demo::parser::{Encode, ParseBitSkip};
1415
use crate::{Parse, ParserState, Result, Stream};
1516
use bitbuffer::{BitRead, BitWrite, BitWriteStream, LittleEndian};
@@ -27,6 +28,7 @@ pub mod tempentities;
2728
pub mod usermessage;
2829
pub mod voice;
2930
pub mod prefetch;
31+
mod serverinfo;
3032

3133
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema_repr))]
3234
#[derive(

src/demo/message/serverinfo.rs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
use crate::demo::parser::{Encode, ParseBitSkip};
2+
use crate::{Parse, ParserState, Result, Stream};
3+
use bitbuffer::{BitRead, BitWrite, BitWriteStream, LittleEndian};
4+
use serde::{Deserialize, Serialize};
5+
6+
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
7+
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
8+
pub 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+
27+
impl<'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+
65+
impl<'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().unwrap_or_default()
73+
+ <ServerInfoMessagePart2 as BitRead<LittleEndian>>::bit_size().unwrap_or_default()
74+
+ version_dependent_size;
75+
stream.skip_bits(size)?;
76+
Ok(())
77+
}
78+
}
79+
80+
impl Encode for ServerInfoMessage {
81+
fn encode(&self, stream: &mut BitWriteStream<LittleEndian>, state: &ParserState) -> Result<()> {
82+
let part1 = ServerInfoMessagePart1 {
83+
version: self.version,
84+
server_count: self.server_count,
85+
stv: self.stv,
86+
dedicated: self.dedicated,
87+
max_crc: self.max_crc,
88+
max_classes: self.max_classes,
89+
};
90+
part1.write(stream)?;
91+
if state.protocol_version > 17 {
92+
self.map_hash.write(stream)?;
93+
} else {
94+
let crc = u32::from_le_bytes([
95+
self.map_hash[0],
96+
self.map_hash[1],
97+
self.map_hash[2],
98+
self.map_hash[3],
99+
]);
100+
crc.write(stream)?;
101+
};
102+
let part2 = ServerInfoMessagePart2 {
103+
player_slot: self.player_slot,
104+
max_player_count: self.max_player_count,
105+
interval_per_tick: self.interval_per_tick,
106+
platform: self.platform.clone(),
107+
game: self.game.clone(),
108+
map: self.map.clone(),
109+
skybox: self.skybox.clone(),
110+
server_name: self.server_name.clone(),
111+
};
112+
part2.write(stream)?;
113+
if state.protocol_version > 15 {
114+
self.replay.write(stream)?;
115+
}
116+
Ok(())
117+
}
118+
}
119+
120+
#[derive(BitRead, BitWrite)]
121+
pub struct ServerInfoMessagePart1 {
122+
pub version: u16,
123+
pub server_count: u32,
124+
pub stv: bool,
125+
pub dedicated: bool,
126+
pub max_crc: u32,
127+
pub max_classes: u16,
128+
}
129+
130+
#[derive(BitRead, BitWrite)]
131+
pub struct ServerInfoMessagePart2 {
132+
pub player_slot: u8,
133+
pub max_player_count: u8,
134+
pub interval_per_tick: f32,
135+
#[size = 1]
136+
pub platform: String,
137+
pub game: String,
138+
pub map: String,
139+
pub skybox: String,
140+
pub server_name: String,
141+
}

0 commit comments

Comments
 (0)