Skip to main content

tf_demo_parser/demo/parser/
state.rs

1use fnv::FnvHashMap;
2use std::borrow::Cow;
3use std::collections::HashMap;
4
5use crate::demo::gamevent::GameEventDefinition;
6
7use crate::demo::message::packetentities::{
8    BaselineIndex, EntityId, PacketEntitiesMessage, PacketEntity, UpdateType,
9};
10use crate::demo::message::stringtable::StringTableMeta;
11use crate::demo::message::{Message, MessageType, log_base2};
12use crate::demo::packet::datatable::{
13    ClassId, ParseSendTable, SendTable, SendTableName, ServerClass,
14};
15use crate::demo::packet::stringtable::StringTableEntry;
16
17use crate::demo::data::DemoTick;
18use crate::demo::sendprop::{SendProp, SendPropIdentifier};
19use crate::nullhasher::NullHasherBuilder;
20use crate::{Result, Stream};
21use serde::{Deserialize, Serialize};
22use std::cell::RefCell;
23#[cfg(feature = "trace")]
24use tracing::warn;
25
26#[derive(Default, Clone, Serialize, Deserialize)]
27pub struct DemoMeta {
28    pub version: u16,
29    pub game: String,
30    pub interval_per_tick: f32,
31}
32
33#[derive(Clone)]
34pub struct ParserState {
35    pub static_baselines: HashMap<ClassId, StaticBaseline, NullHasherBuilder>,
36    pub parsed_static_baselines: RefCell<HashMap<ClassId, Vec<SendProp>, NullHasherBuilder>>,
37    pub event_definitions: Vec<GameEventDefinition>,
38    pub string_tables: Vec<StringTableMeta>,
39    pub entity_classes: HashMap<EntityId, ClassId, NullHasherBuilder>,
40    // indexed by ClassId
41    pub send_tables: Vec<SendTable>,
42    pub server_classes: Vec<ServerClass>,
43    pub server_class_bits: usize,
44    pub instance_baselines: [Baseline; 2],
45    pub demo_meta: DemoMeta,
46    analyser_handles: fn(message_type: MessageType) -> bool,
47    handle_entities: bool,
48    parse_all: bool,
49    pub protocol_version: u32,
50}
51
52#[derive(Clone)]
53pub struct StaticBaseline {
54    pub class_id: ClassId,
55    pub raw: Stream<'static>,
56}
57
58impl StaticBaseline {
59    fn new(class_id: ClassId, raw: Stream<'static>) -> Self {
60        StaticBaseline { class_id, raw }
61    }
62
63    pub fn parse(&self, send_table: &SendTable) -> Result<Vec<SendProp>> {
64        let mut props = Vec::with_capacity(8);
65        PacketEntitiesMessage::read_update(
66            &mut self.raw.clone(),
67            send_table,
68            &mut props,
69            0u32.into(),
70        )?;
71        Ok(props)
72    }
73}
74
75impl ParserState {
76    pub fn new(
77        protocol_version: u32,
78        analyser_handles: fn(message_type: MessageType) -> bool,
79        parse_all: bool,
80    ) -> Self {
81        ParserState {
82            static_baselines: HashMap::with_hasher(NullHasherBuilder),
83            parsed_static_baselines: RefCell::new(HashMap::with_hasher(NullHasherBuilder)),
84            event_definitions: Vec::new(),
85            string_tables: Vec::new(),
86            entity_classes: HashMap::with_hasher(NullHasherBuilder),
87            send_tables: Vec::new(),
88            server_classes: Vec::new(),
89            server_class_bits: 0,
90            instance_baselines: [Baseline::default(), Baseline::default()],
91            demo_meta: DemoMeta::default(),
92            analyser_handles,
93            handle_entities: analyser_handles(MessageType::PacketEntities) || parse_all,
94            parse_all,
95            protocol_version,
96        }
97    }
98
99    pub fn set_server_classes(&mut self, server_classes: Vec<ServerClass>) {
100        self.server_class_bits = log_base2(server_classes.len()) as usize + 1;
101        self.server_classes = server_classes;
102    }
103
104    pub fn get_static_baseline(
105        &self,
106        class_id: ClassId,
107        send_table: &SendTable,
108    ) -> Result<Vec<SendProp>> {
109        match self.static_baselines.get(&class_id) {
110            Some(static_baseline) => static_baseline.parse(send_table),
111            None => {
112                #[cfg(feature = "trace")]
113                warn!(
114                    class_id = display(class_id),
115                    "class without static baseline"
116                );
117                Ok(Vec::new())
118            }
119        }
120        // let mut cached = self.parsed_static_baselines.borrow_mut();
121        // Ok(match cached.entry(class_id) {
122        //     Entry::Occupied(entry) => entry.get().as_slice(),
123        //     Entry::Vacant(entry) => match self.static_baselines.get(&class_id) {
124        //         Some(static_baseline) => {
125        //             let props = static_baseline.parse(send_table)?;
126        //             entry.insert(props).as_slice()
127        //         }
128        //         None => {
129        //             #[cfg(feature = "trace")]
130        //             warn!(
131        //                 class_id = display(class_id),
132        //                 "class without static baseline"
133        //             );
134        //             &[]
135        //         }
136        //     },
137        // })
138    }
139
140    fn get_instance_baseline(&self, index: BaselineIndex) -> &Baseline {
141        match index {
142            BaselineIndex::First => &self.instance_baselines[0],
143            BaselineIndex::Second => &self.instance_baselines[1],
144        }
145    }
146
147    fn get_instance_baseline_mut(&mut self, index: BaselineIndex) -> &mut Baseline {
148        match index {
149            BaselineIndex::First => &mut self.instance_baselines[0],
150            BaselineIndex::Second => &mut self.instance_baselines[1],
151        }
152    }
153
154    pub fn get_baseline(
155        &self,
156        baseline_index: BaselineIndex,
157        entity_index: EntityId,
158        class_id: ClassId,
159        send_table: &SendTable,
160        is_delta: bool,
161    ) -> Result<Cow<'_, [SendProp]>> {
162        match self.get_instance_baseline(baseline_index).get(entity_index) {
163            Some(baseline) if baseline.server_class == class_id && is_delta => {
164                Ok(Cow::Borrowed(&baseline.props))
165            }
166            _ => match self.static_baselines.get(&class_id) {
167                Some(_static_baseline) => {
168                    Ok(Cow::Owned(self.get_static_baseline(class_id, send_table)?))
169                }
170                None => {
171                    #[cfg(feature = "trace")]
172                    warn!(
173                        class_id = display(class_id),
174                        "class without static baseline"
175                    );
176                    Ok(Cow::Owned(Vec::new()))
177                }
178            },
179        }
180    }
181
182    pub fn handle_data_table(
183        &mut self,
184        parse_tables: &[ParseSendTable],
185        server_classes: Vec<ServerClass>,
186    ) -> Result<()> {
187        if self.handle_entities {
188            let mut send_tables: FnvHashMap<SendTableName, SendTable> = parse_tables
189                .iter()
190                .map(|parse_table| {
191                    let flat = parse_table.flatten_props(parse_tables);
192                    Ok((
193                        parse_table.name.clone(),
194                        SendTable {
195                            name: parse_table.name.clone(),
196                            needs_decoder: parse_table.needs_decoder,
197                            flattened_props: flat?,
198                        },
199                    ))
200                })
201                .collect::<Result<_>>()?;
202
203            self.set_server_classes(server_classes);
204
205            self.send_tables.reserve(self.server_classes.len());
206
207            for class in self.server_classes.iter() {
208                if let Some(table) = send_tables.remove(&class.data_table) {
209                    self.send_tables.push(table);
210                } else {
211                    #[cfg(feature = "trace")]
212                    warn!(class = debug(class), "class without table");
213                }
214            }
215        }
216
217        Ok(())
218    }
219
220    pub fn handle_string_table_meta(&mut self, table: StringTableMeta) {
221        self.string_tables.push(table);
222    }
223
224    pub fn should_parse_message(&self, message_type: MessageType) -> bool {
225        self.parse_all
226            || if message_type == MessageType::PacketEntities {
227                self.handle_entities
228            } else {
229                Self::does_handle(message_type) || (self.analyser_handles)(message_type)
230            }
231    }
232
233    pub fn does_handle(message_type: MessageType) -> bool {
234        matches!(
235            message_type,
236            MessageType::ServerInfo
237                | MessageType::NetTick
238                | MessageType::GameEventList
239                | MessageType::CreateStringTable
240                | MessageType::PacketEntities
241                | MessageType::UpdateStringTable
242        )
243    }
244
245    pub fn handle_message(&mut self, message: Message, _tick: DemoTick) {
246        match message {
247            Message::ServerInfo(message) => {
248                self.demo_meta.version = message.version;
249                self.demo_meta.game = message.game;
250                self.demo_meta.interval_per_tick = message.interval_per_tick;
251            }
252            Message::GameEventList(message) => {
253                self.event_definitions = message.event_list;
254            }
255            Message::PacketEntities(ent_message) => {
256                for removed in ent_message.removed_entities.iter() {
257                    self.entity_classes.remove(removed);
258                }
259
260                for entity in ent_message.entities.iter() {
261                    if entity.update_type == UpdateType::Delete {
262                        self.entity_classes.remove(&entity.entity_index);
263                    }
264                    self.entity_classes
265                        .insert(entity.entity_index, entity.server_class);
266                }
267
268                if ent_message.updated_base_line {
269                    let old_index = ent_message.base_line;
270                    let new_index = old_index.other();
271                    let [baseline1, baseline2] = &mut self.instance_baselines;
272                    if old_index == BaselineIndex::First {
273                        baseline2.copy_from(baseline1);
274                    } else {
275                        baseline1.copy_from(baseline2);
276                    }
277
278                    for entity in ent_message.entities {
279                        if entity.update_type == UpdateType::Enter {
280                            let updated_baseline = match self
281                                .get_instance_baseline(old_index)
282                                .get(entity.entity_index)
283                            {
284                                Some(baseline_entity)
285                                    if baseline_entity.server_class == entity.server_class
286                                        && ent_message.delta.is_some() =>
287                                {
288                                    let mut updated_baseline = baseline_entity.clone();
289                                    updated_baseline.apply_update(&entity.props);
290                                    updated_baseline
291                                }
292                                _ => entity.into(),
293                            };
294                            self.get_instance_baseline_mut(new_index)
295                                .set(updated_baseline);
296                        }
297                    }
298                }
299            }
300            _ => {}
301        }
302    }
303
304    pub fn handle_string_entry(&mut self, table: &str, _index: usize, entry: &StringTableEntry) {
305        if table == "instancebaseline"
306            && let (Some(extra), Ok(class_id)) = (&entry.extra_data, entry.text().parse()) {
307                let baseline = StaticBaseline::new(class_id, extra.data.to_owned());
308                self.static_baselines.insert(class_id, baseline);
309                self.parsed_static_baselines.borrow_mut().remove(&class_id);
310            }
311    }
312
313    pub fn index_for_prop(&self, class: ClassId, prop: SendPropIdentifier) -> Option<u32> {
314        let send_table = self.send_tables.get(usize::from(class))?;
315        send_table
316            .flattened_props
317            .iter()
318            .enumerate()
319            .find(|(_i, def)| def.identifier == prop)
320            .map(|(index, _)| index as u32)
321    }
322}
323
324#[derive(Clone)]
325pub struct Baseline {
326    instances: Vec<Option<BaselineEntity>>,
327}
328
329impl Default for Baseline {
330    fn default() -> Self {
331        Baseline {
332            instances: vec![None; 2048],
333        }
334    }
335}
336
337impl Baseline {
338    pub fn get(&self, index: EntityId) -> Option<&BaselineEntity> {
339        self.instances
340            .get(usize::from(index))
341            .and_then(|opt| opt.as_ref())
342    }
343
344    fn set(&mut self, entity: BaselineEntity) {
345        if let Some(instance) = self.instances.get_mut(usize::from(entity.entity_id)) {
346            *instance = Some(entity);
347        }
348    }
349
350    pub fn keys(&self) -> impl Iterator<Item = EntityId> + '_ {
351        self.instances
352            .iter()
353            .filter_map(|entity| entity.as_ref().map(|entity| entity.entity_id))
354    }
355
356    pub fn into_values(self) -> impl Iterator<Item = PacketEntity> {
357        self.instances
358            .into_iter()
359            .filter_map(|entity| entity.map(|entity| entity.into()))
360    }
361
362    pub fn contains(&self, index: EntityId) -> bool {
363        self.get(index).is_some()
364    }
365
366    fn copy_from(&mut self, other: &Baseline) {
367        for (ent, other_ent) in self.instances.iter_mut().zip(other.instances.iter()) {
368            match (ent, other_ent) {
369                (ent, Some(other_ent)) => *ent = Some(other_ent.clone()),
370                (ent, None) => {
371                    *ent = None;
372                }
373            }
374        }
375    }
376}
377
378#[derive(Clone)]
379pub struct BaselineEntity {
380    pub entity_id: EntityId,
381    pub server_class: ClassId,
382    pub props: Vec<SendProp>,
383    pub serial: u32,
384}
385
386impl BaselineEntity {
387    fn mut_prop_by_identifier(&mut self, index: &SendPropIdentifier) -> Option<&mut SendProp> {
388        self.props.iter_mut().find(|prop| prop.identifier == *index)
389    }
390
391    pub fn apply_update(&mut self, props: &[SendProp]) {
392        for prop in props {
393            match self.mut_prop_by_identifier(&prop.identifier) {
394                Some(existing_prop) => existing_prop.value = prop.value.clone(),
395                None => self.props.push(prop.clone()),
396            }
397        }
398    }
399}
400
401impl From<PacketEntity> for BaselineEntity {
402    fn from(entity: PacketEntity) -> Self {
403        BaselineEntity {
404            entity_id: entity.entity_index,
405            server_class: entity.server_class,
406            props: entity.props,
407            serial: entity.serial_number,
408        }
409    }
410}
411
412impl From<BaselineEntity> for PacketEntity {
413    fn from(baseline: BaselineEntity) -> Self {
414        PacketEntity {
415            server_class: baseline.server_class,
416            entity_index: baseline.entity_id,
417            props: baseline.props,
418            in_pvs: false,
419            update_type: UpdateType::Enter,
420            serial_number: baseline.serial,
421            delay: None,
422            delta: None,
423            baseline_index: BaselineIndex::First,
424        }
425    }
426}