|
| 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