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