tf_demo_parser/demo/data/
userinfo.rs1use crate::demo::message::packetentities::EntityId;
2use crate::demo::packet::stringtable::{ExtraData, StringTableEntry};
3use crate::demo::parser::analyser::UserId;
4use crate::{ReadResult, Stream};
5use bitbuffer::{BitRead, BitReadBuffer, BitReadStream, BitWrite, BitWriteStream, LittleEndian};
6
7#[derive(BitRead, Debug)]
8struct RawPlayerInfo {
9 pub name_bytes: [u8; 32],
10 pub user_id: u32,
11 #[size = 32]
12 pub steam_id: String,
13 pub extra: u32, pub friends_id: u32,
15 pub friends_name_bytes: [u8; 32], pub is_fake_player: u8,
17 pub is_hl_tv: u8,
18 pub is_replay: u8,
19 pub custom_file: [u32; 4],
20 pub files_downloaded: u32,
21 pub more_extra: u8,
22}
23
24#[derive(BitWrite, Debug, Clone, Default)]
25pub struct PlayerInfo {
26 #[size = 32]
27 pub name: String,
28 pub user_id: UserId,
29 #[size = 32]
30 pub steam_id: String,
31 pub extra: u32, pub friends_id: u32,
33 pub friends_name_bytes: [u8; 32], pub is_fake_player: u8,
35 pub is_hl_tv: u8,
36 pub is_replay: u8,
37 pub custom_file: [u32; 4],
38 pub files_downloaded: u32,
39 pub more_extra: u8,
40}
41
42impl From<RawPlayerInfo> for PlayerInfo {
43 fn from(raw: RawPlayerInfo) -> Self {
44 PlayerInfo {
45 name: String::from_utf8_lossy(&raw.name_bytes)
46 .trim_end_matches('\0')
47 .to_string(),
48 user_id: raw.user_id.into(),
49 steam_id: raw.steam_id,
50 extra: raw.extra,
51 friends_id: raw.friends_id,
52 friends_name_bytes: raw.friends_name_bytes,
53 is_fake_player: raw.is_fake_player,
54 is_hl_tv: raw.is_hl_tv,
55 is_replay: raw.is_replay,
56 custom_file: raw.custom_file,
57 files_downloaded: raw.files_downloaded,
58 more_extra: raw.more_extra,
59 }
60 }
61}
62
63#[derive(Clone, Debug, Default)]
64pub struct UserInfo {
65 pub entity_id: EntityId,
66 pub player_info: PlayerInfo,
67}
68
69impl UserInfo {
70 pub fn parse_from_string_table(
71 index: u16,
72 text: Option<&str>,
73 data: Option<Stream>,
74 ) -> ReadResult<Option<Self>> {
75 if let Some(mut data) = data {
76 let raw_info: RawPlayerInfo = data.read()?;
78
79 match text
80 .map(|text| text.parse::<u32>().map(|id| (id + 1).into()))
81 .unwrap_or_else(|| Ok((index as u32 + 1).into()))
82 {
83 Ok(entity_id) if !raw_info.steam_id.is_empty() => Ok(Some(UserInfo {
84 player_info: raw_info.into(),
85 entity_id,
86 })),
87 _ => Ok(None),
88 }
89 } else {
90 Ok(None)
91 }
92 }
93
94 pub fn encode_to_string_table(&self) -> ReadResult<StringTableEntry<'static>> {
95 let text = format!("{}", self.entity_id);
96 let mut extra_data = Vec::with_capacity(132);
97 {
98 let mut stream = BitWriteStream::new(&mut extra_data, LittleEndian);
99 self.player_info.write(&mut stream)?;
100 }
101
102 Ok(StringTableEntry {
103 text: Some(text.into()),
104 extra_data: Some(ExtraData::new(BitReadStream::new(
105 BitReadBuffer::new_owned(extra_data, LittleEndian),
106 ))),
107 })
108 }
109}