Skip to main content

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