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