tf_demo_parser/demo/packet/
datatable.rs

1use crate::demo::data::DemoTick;
2use crate::demo::parser::MalformedSendPropDefinitionError;
3use crate::demo::sendprop::{
4    RawSendPropDefinition, SendPropDefinition, SendPropFlag, SendPropIdentifier, SendPropType,
5};
6use crate::{Parse, ParseError, ParserState, Result, Stream};
7use bitbuffer::{
8    BitRead, BitReadStream, BitWrite, BitWriteSized, BitWriteStream, Endianness, LittleEndian,
9};
10use parse_display::{Display, FromStr};
11use serde::{Deserialize, Serialize};
12use std::borrow::Cow;
13use std::cmp::min;
14use std::convert::TryFrom;
15use std::iter::once;
16use std::ops::Deref;
17
18#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
19#[derive(
20    BitRead,
21    BitWrite,
22    Debug,
23    Clone,
24    Copy,
25    PartialEq,
26    Eq,
27    Hash,
28    Ord,
29    PartialOrd,
30    Display,
31    FromStr,
32    Serialize,
33    Deserialize,
34)]
35pub struct ClassId(u16);
36
37impl From<u16> for ClassId {
38    fn from(int: u16) -> Self {
39        ClassId(int)
40    }
41}
42
43impl From<ClassId> for usize {
44    fn from(class: ClassId) -> Self {
45        class.0 as usize
46    }
47}
48
49impl From<ClassId> for u16 {
50    fn from(class: ClassId) -> Self {
51        class.0
52    }
53}
54
55impl PartialEq<u16> for ClassId {
56    fn eq(&self, other: &u16) -> bool {
57        self.0 == *other
58    }
59}
60
61#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
62#[derive(BitRead, BitWrite, PartialEq, Eq, Hash, Debug, Serialize, Deserialize, Clone, Display)]
63pub struct ServerClassName(String);
64
65impl ServerClassName {
66    pub fn as_str(&self) -> &str {
67        self.0.as_str()
68    }
69}
70
71impl From<String> for ServerClassName {
72    fn from(value: String) -> Self {
73        Self(value)
74    }
75}
76
77impl From<&str> for ServerClassName {
78    fn from(value: &str) -> Self {
79        Self(value.into())
80    }
81}
82
83impl PartialEq<&str> for ServerClassName {
84    fn eq(&self, other: &&str) -> bool {
85        self.as_str() == *other
86    }
87}
88
89impl AsRef<str> for ServerClassName {
90    fn as_ref(&self) -> &str {
91        self.0.as_ref()
92    }
93}
94
95impl Deref for ServerClassName {
96    type Target = str;
97
98    fn deref(&self) -> &Self::Target {
99        self.0.deref()
100    }
101}
102
103#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
104#[derive(BitRead, BitWrite, Debug, Clone, PartialEq, Serialize, Deserialize)]
105pub struct ServerClass {
106    pub id: ClassId,
107    pub name: ServerClassName,
108    pub data_table: SendTableName,
109}
110
111#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
112#[derive(
113    BitWrite,
114    PartialEq,
115    Eq,
116    Hash,
117    Debug,
118    Serialize,
119    Deserialize,
120    Clone,
121    Display,
122    PartialOrd,
123    Ord,
124    Default,
125)]
126pub struct SendTableName(Cow<'static, str>);
127
128impl SendTableName {
129    pub fn as_str(&self) -> &str {
130        self.0.as_ref()
131    }
132}
133
134impl<E: Endianness> BitRead<'_, E> for SendTableName {
135    fn read(stream: &mut BitReadStream<'_, E>) -> bitbuffer::Result<Self> {
136        String::read(stream).map(SendTableName::from)
137    }
138}
139
140impl From<String> for SendTableName {
141    fn from(value: String) -> Self {
142        Self(Cow::Owned(value))
143    }
144}
145
146impl From<&'static str> for SendTableName {
147    fn from(value: &'static str) -> Self {
148        Self(Cow::Borrowed(value))
149    }
150}
151
152impl PartialEq<&str> for SendTableName {
153    fn eq(&self, other: &&str) -> bool {
154        self.as_str() == *other
155    }
156}
157
158impl AsRef<str> for SendTableName {
159    fn as_ref(&self) -> &str {
160        self.0.as_ref()
161    }
162}
163
164impl Deref for SendTableName {
165    type Target = str;
166
167    fn deref(&self) -> &Self::Target {
168        self.0.deref()
169    }
170}
171
172#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
173#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
174pub struct ParseSendTable {
175    pub name: SendTableName,
176    pub props: Vec<RawSendPropDefinition>,
177    pub needs_decoder: bool,
178}
179
180impl Parse<'_> for ParseSendTable {
181    fn parse(stream: &mut Stream, _state: &ParserState) -> Result<Self> {
182        let needs_decoder = stream.read()?;
183        let name: SendTableName = stream.read()?;
184        let prop_count = stream.read_int(10)?;
185
186        let mut array_element_prop = None;
187        let mut props = Vec::with_capacity(min(prop_count, 128));
188
189        for _ in 0..prop_count {
190            let prop: RawSendPropDefinition = RawSendPropDefinition::read(stream, &name)?;
191            if prop.flags.contains(SendPropFlag::InsideArray) {
192                if array_element_prop.is_some() || prop.flags.contains(SendPropFlag::ChangesOften) {
193                    return Err(MalformedSendPropDefinitionError::ArrayChangesOften.into());
194                }
195                array_element_prop = Some(prop);
196            } else if let Some(array_element) = array_element_prop {
197                if prop.prop_type != SendPropType::Array {
198                    return Err(MalformedSendPropDefinitionError::UntypedArray.into());
199                }
200                array_element_prop = None;
201                props.push(prop.with_array_property(array_element));
202            } else {
203                props.push(prop);
204            }
205        }
206
207        Ok(ParseSendTable {
208            name,
209            props,
210            needs_decoder,
211        })
212    }
213}
214
215impl BitWrite<LittleEndian> for ParseSendTable {
216    fn write(&self, stream: &mut BitWriteStream<LittleEndian>) -> bitbuffer::Result<()> {
217        self.needs_decoder.write(stream)?;
218        self.name.write(stream)?;
219
220        let prop_count: u16 = self
221            .props
222            .iter()
223            .map(|prop| match prop.array_property {
224                Some(_) => 2,
225                None => 1,
226            })
227            .sum();
228        prop_count.write_sized(stream, 10)?;
229
230        for prop in self
231            .props
232            .iter()
233            .flat_map(|prop| prop.array_property.as_deref().into_iter().chain(once(prop)))
234        {
235            prop.write(stream)?;
236        }
237
238        Ok(())
239    }
240}
241
242#[test]
243fn test_parse_send_table_roundtrip() {
244    use crate::demo::sendprop::SendPropFlags;
245
246    let state = ParserState::new(24, |_| false, false);
247    crate::test_roundtrip_encode(
248        ParseSendTable {
249            name: "foo".into(),
250            props: vec![],
251            needs_decoder: true,
252        },
253        &state,
254    );
255    crate::test_roundtrip_encode(
256        ParseSendTable {
257            name: "table1".into(),
258            props: vec![
259                RawSendPropDefinition {
260                    prop_type: SendPropType::Float,
261                    name: "prop1".into(),
262                    identifier: SendPropIdentifier::new("table1", "prop1"),
263                    flags: SendPropFlags::default() | SendPropFlag::ChangesOften,
264                    table_name: None,
265                    low_value: Some(0.0),
266                    high_value: Some(128.0),
267                    bit_count: Some(10),
268                    element_count: None,
269                    array_property: None,
270                    original_bit_count: Some(10),
271                },
272                RawSendPropDefinition {
273                    prop_type: SendPropType::Array,
274                    name: "prop2".into(),
275                    identifier: SendPropIdentifier::new("table1", "prop2"),
276                    flags: SendPropFlags::default(),
277                    table_name: None,
278                    low_value: None,
279                    high_value: None,
280                    bit_count: None,
281                    element_count: Some(10),
282                    array_property: Some(Box::new(RawSendPropDefinition {
283                        prop_type: SendPropType::Int,
284                        name: "prop3".into(),
285                        identifier: SendPropIdentifier::new("table1", "prop3"),
286                        flags: SendPropFlags::default()
287                            | SendPropFlag::InsideArray
288                            | SendPropFlag::NoScale,
289                        table_name: None,
290                        low_value: Some(i32::MIN as f32),
291                        high_value: Some(i32::MAX as f32),
292                        bit_count: Some(32),
293                        element_count: None,
294                        array_property: None,
295                        original_bit_count: Some(32),
296                    })),
297                    original_bit_count: None,
298                },
299                RawSendPropDefinition {
300                    prop_type: SendPropType::DataTable,
301                    name: "prop1".into(),
302                    identifier: SendPropIdentifier::new("table1", "prop1"),
303                    flags: SendPropFlags::default() | SendPropFlag::Exclude,
304                    table_name: Some("table2".into()),
305                    low_value: None,
306                    high_value: None,
307                    bit_count: None,
308                    element_count: None,
309                    array_property: None,
310                    original_bit_count: None,
311                },
312            ],
313            needs_decoder: true,
314        },
315        &state,
316    );
317}
318
319impl ParseSendTable {
320    pub fn flatten_props(&self, tables: &[ParseSendTable]) -> Result<Vec<SendPropDefinition>> {
321        let mut flat = Vec::with_capacity(32);
322        self.push_props_end(
323            tables,
324            &self.get_excludes(tables),
325            &mut flat,
326            &mut Vec::with_capacity(16),
327        )?;
328
329        // sort often changed props before the others
330        let mut start = 0;
331        for i in 0..flat.len() {
332            #[allow(clippy::indexing_slicing)]
333            if flat[i].parse_definition.changes_often() {
334                if i != start {
335                    flat.swap(i, start);
336                }
337                start += 1;
338            }
339        }
340
341        Ok(flat)
342    }
343
344    fn get_excludes<'a>(&'a self, tables: &'a [ParseSendTable]) -> Vec<SendPropIdentifier> {
345        let mut excludes = Vec::with_capacity(8);
346
347        self.build_excludes(tables, &mut Vec::with_capacity(8), &mut excludes);
348
349        excludes
350    }
351
352    fn build_excludes<'a>(
353        &'a self,
354        tables: &'a [ParseSendTable],
355        processed_tables: &mut Vec<&'a SendTableName>,
356        excludes: &mut Vec<SendPropIdentifier>,
357    ) {
358        processed_tables.push(&self.name);
359
360        for prop in self.props.iter() {
361            if let Some(exclude_table) = prop.get_exclude_table() {
362                excludes.push(SendPropIdentifier::new(
363                    exclude_table.as_str(),
364                    prop.name.as_str(),
365                ))
366            } else if let Some(table) = prop.get_data_table(tables) {
367                if !processed_tables.contains(&&table.name) {
368                    table.build_excludes(tables, processed_tables, excludes);
369                }
370            }
371        }
372    }
373
374    // TODO: below is a direct port from the js which is a direct port from C++ and not very optimal
375    fn push_props_end<'a>(
376        &'a self,
377        tables: &'a [ParseSendTable],
378        excludes: &[SendPropIdentifier],
379        props: &mut Vec<SendPropDefinition>,
380        table_stack: &mut Vec<&'a SendTableName>,
381    ) -> Result<()> {
382        let mut local_props = Vec::new();
383
384        self.push_props_collapse(tables, excludes, &mut local_props, props, table_stack)?;
385        props.extend_from_slice(&local_props);
386        Ok(())
387    }
388
389    fn push_props_collapse<'a>(
390        &'a self,
391        tables: &'a [ParseSendTable],
392        excludes: &[SendPropIdentifier],
393        local_props: &mut Vec<SendPropDefinition>,
394        props: &mut Vec<SendPropDefinition>,
395        table_stack: &mut Vec<&'a SendTableName>,
396    ) -> Result<()> {
397        table_stack.push(&self.name);
398
399        let result = self
400            .props
401            .iter()
402            .filter(|prop| !prop.is_exclude())
403            .filter(|prop| !excludes.iter().any(|exclude| *exclude == prop.identifier()))
404            .try_for_each(|prop| {
405                if let Some(table) = prop.get_data_table(tables) {
406                    if !table_stack.contains(&&table.name) {
407                        if prop.flags.contains(SendPropFlag::Collapsible) {
408                            table.push_props_collapse(
409                                tables,
410                                excludes,
411                                local_props,
412                                props,
413                                table_stack,
414                            )?;
415                        } else {
416                            table.push_props_end(tables, excludes, props, table_stack)?;
417                        }
418                    }
419                } else {
420                    local_props.push(SendPropDefinition::try_from(prop)?);
421                }
422                Ok(())
423            });
424
425        table_stack.pop();
426
427        result
428    }
429}
430
431#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
432#[derive(Debug, Clone, Serialize, Deserialize)]
433pub struct SendTable {
434    pub name: SendTableName,
435    pub needs_decoder: bool,
436    pub flattened_props: Vec<SendPropDefinition>,
437}
438
439#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
440#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
441pub struct DataTablePacket {
442    pub tick: DemoTick,
443    pub tables: Vec<ParseSendTable>,
444    pub server_classes: Vec<ServerClass>,
445}
446
447impl Parse<'_> for DataTablePacket {
448    fn parse(stream: &mut Stream, state: &ParserState) -> Result<Self> {
449        let tick = stream.read()?;
450        let len = stream.read_int::<usize>(32)?;
451        let mut packet_data = stream.read_bits(len * 8)?;
452
453        let mut tables = Vec::new();
454        while packet_data.read_bool()? {
455            let table = ParseSendTable::parse(&mut packet_data, state)?;
456            tables.push(table);
457        }
458
459        let server_class_count = packet_data.read_int(16)?;
460        let server_classes = packet_data.read_sized(server_class_count)?;
461
462        if packet_data.bits_left() > 7 {
463            Err(ParseError::DataRemaining(packet_data.bits_left()))
464        } else {
465            Ok(DataTablePacket {
466                tick,
467                tables,
468                server_classes,
469            })
470        }
471    }
472}
473
474impl BitWrite<LittleEndian> for DataTablePacket {
475    fn write(&self, stream: &mut BitWriteStream<LittleEndian>) -> bitbuffer::Result<()> {
476        self.tick.write(stream)?;
477        stream.reserve_byte_length(32, |stream| {
478            for table in self.tables.iter() {
479                true.write(stream)?;
480                table.write(stream)?;
481            }
482            false.write(stream)?;
483
484            (self.server_classes.len() as u16).write(stream)?;
485            self.server_classes.write(stream)?;
486
487            Ok(())
488        })
489    }
490}
491
492#[test]
493fn test_data_table_packet_roundtrip() {
494    use crate::demo::sendprop::SendPropFlags;
495
496    let state = ParserState::new(24, |_| false, false);
497    crate::test_roundtrip_encode(
498        DataTablePacket {
499            tick: 123.into(),
500            tables: vec![],
501            server_classes: vec![],
502        },
503        &state,
504    );
505
506    let table1 = ParseSendTable {
507        name: "table1".into(),
508        props: vec![
509            RawSendPropDefinition {
510                prop_type: SendPropType::Float,
511                name: "prop1".into(),
512                identifier: SendPropIdentifier::new("table1", "prop1"),
513                flags: SendPropFlags::default() | SendPropFlag::ChangesOften,
514                table_name: None,
515                low_value: Some(0.0),
516                high_value: Some(128.0),
517                bit_count: Some(10),
518                element_count: None,
519                array_property: None,
520                original_bit_count: Some(10),
521            },
522            RawSendPropDefinition {
523                prop_type: SendPropType::Array,
524                name: "prop2".into(),
525                identifier: SendPropIdentifier::new("table1", "prop2"),
526                flags: SendPropFlags::default(),
527                table_name: None,
528                low_value: None,
529                high_value: None,
530                bit_count: None,
531                element_count: Some(10),
532                array_property: Some(Box::new(RawSendPropDefinition {
533                    prop_type: SendPropType::Int,
534                    name: "prop3".into(),
535                    identifier: SendPropIdentifier::new("table1", "prop3"),
536                    flags: SendPropFlags::default()
537                        | SendPropFlag::InsideArray
538                        | SendPropFlag::NoScale,
539                    table_name: None,
540                    low_value: Some(i32::MIN as f32),
541                    high_value: Some(i32::MAX as f32),
542                    bit_count: Some(32),
543                    element_count: None,
544                    array_property: None,
545                    original_bit_count: Some(32),
546                })),
547                original_bit_count: None,
548            },
549            RawSendPropDefinition {
550                prop_type: SendPropType::DataTable,
551                name: "prop1".into(),
552                identifier: SendPropIdentifier::new("table1", "prop1"),
553                flags: SendPropFlags::default() | SendPropFlag::Exclude,
554                table_name: Some("table2".into()),
555                low_value: None,
556                high_value: None,
557                bit_count: None,
558                element_count: None,
559                array_property: None,
560                original_bit_count: None,
561            },
562        ],
563        needs_decoder: true,
564    };
565    let table2 = ParseSendTable {
566        name: "table2".into(),
567        props: vec![RawSendPropDefinition {
568            prop_type: SendPropType::Float,
569            name: "prop1".into(),
570            identifier: SendPropIdentifier::new("table2", "prop1"),
571            flags: SendPropFlags::default() | SendPropFlag::ChangesOften,
572            table_name: None,
573            low_value: Some(0.0),
574            high_value: Some(128.0),
575            bit_count: Some(10),
576            element_count: None,
577            array_property: None,
578            original_bit_count: Some(10),
579        }],
580        needs_decoder: true,
581    };
582    crate::test_roundtrip_encode(
583        DataTablePacket {
584            tick: 1.into(),
585            tables: vec![table1, table2],
586            server_classes: vec![
587                ServerClass {
588                    id: ClassId(0),
589                    name: "class1".into(),
590                    data_table: "table1".into(),
591                },
592                ServerClass {
593                    id: ClassId(1),
594                    name: "class2".into(),
595                    data_table: "table2".into(),
596                },
597            ],
598        },
599        &state,
600    );
601}