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 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 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}