1use std::collections::HashSet;
2
3use rustc_hash::FxHashMap;
4
5use crate::error::{Error, Result};
6use crate::io::BitReader;
7
8use super::class_info::ClassInfo;
9use super::field_decoder::FieldDecodeContext;
10use super::field_path::{self, FieldPath};
11use super::field_value::FieldValue;
12use super::serializers::{Serializer, SerializerContainer};
13use super::string_tables::StringTableContainer;
14
15use boon_proto::proto::CsvcMsgPacketEntities;
16
17const MAX_EDICT_BITS: u32 = 14;
23const NUM_ENT_ENTRY_BITS: u32 = MAX_EDICT_BITS + 1;
24const NUM_SERIAL_NUM_BITS: u32 = 32 - NUM_ENT_ENTRY_BITS;
25
26pub const ENTITY_HANDLE_INDEX_MASK: u32 = 0x3FFF;
33
34pub const INVALID_ENTITY_HANDLE: u32 = 0x00FF_FFFF;
39
40pub fn protobuf_handle_index(handle: Option<u32>) -> Option<i32> {
52 handle
53 .filter(|&h| h != INVALID_ENTITY_HANDLE)
54 .map(|h| (h & ENTITY_HANDLE_INDEX_MASK) as i32)
55}
56
57const DELTA_UPDATE: u8 = 0b00;
59const DELTA_CREATE: u8 = 0b10;
60const DELTA_LEAVE: u8 = 0b01;
61const DELTA_DELETE: u8 = 0b11;
62
63#[derive(Debug, Clone)]
65pub struct Entity {
66 pub index: i32,
68 pub serial: u32,
70 pub class_id: i32,
72 pub class_name: String,
74 pub fields: FxHashMap<u64, FieldValue>,
76}
77
78impl Entity {
79 fn new(index: i32, class_id: i32, class_name: String) -> Self {
80 Self {
81 index,
82 serial: 0,
83 class_id,
84 class_name,
85 fields: FxHashMap::default(),
86 }
87 }
88
89 #[allow(clippy::needless_range_loop)]
91 fn apply_update(
92 &mut self,
93 br: &mut BitReader,
94 serializer: &Serializer,
95 ctx: &mut FieldDecodeContext,
96 fp_buf: &mut Vec<FieldPath>,
97 ) -> Result<()> {
98 field_path::read_field_paths(br, fp_buf)?;
99
100 for fp_idx in 0..fp_buf.len() {
101 let fp_last = fp_buf[fp_idx].last;
103 let mut field = &serializer.fields[fp_buf[fp_idx].get(0)];
104
105 for i in 1..=fp_last {
106 let idx = fp_buf[fp_idx].get(i);
107 if field.is_dynamic_array() {
108 if let Some(ref fs) = field.field_serializer {
109 field = &fs.fields[0];
110 }
111 } else if let Some(ref fs) = field.field_serializer {
112 field = &fs.fields[idx];
113 } else {
114 break;
115 }
116 }
117
118 let key = fp_buf[fp_idx].pack();
119 let value = field
120 .metadata
121 .decoder
122 .decode(ctx, br)
123 .map_err(|e| Error::Parse {
124 context: format!(
125 "field #{} key={:#x} (type: {}, decoder: {:?}, pos: {}, remaining: {}): {}",
126 fp_idx,
127 key,
128 field.var_type,
129 field.metadata.decoder,
130 br.position(),
131 br.bits_remaining(),
132 e
133 ),
134 })?;
135 self.fields.insert(key, value);
136 }
137
138 Ok(())
139 }
140
141 #[allow(clippy::needless_range_loop)]
144 fn skip_update(
145 br: &mut BitReader,
146 serializer: &Serializer,
147 ctx: &mut FieldDecodeContext,
148 fp_buf: &mut Vec<FieldPath>,
149 ) -> Result<()> {
150 field_path::read_field_paths(br, fp_buf)?;
151
152 for fp_idx in 0..fp_buf.len() {
153 let fp_last = fp_buf[fp_idx].last;
155 let mut field = &serializer.fields[fp_buf[fp_idx].get(0)];
156
157 for i in 1..=fp_last {
158 let idx = fp_buf[fp_idx].get(i);
159 if field.is_dynamic_array() {
160 if let Some(ref fs) = field.field_serializer {
161 field = &fs.fields[0];
162 }
163 } else if let Some(ref fs) = field.field_serializer {
164 field = &fs.fields[idx];
165 } else {
166 break;
167 }
168 }
169
170 field.metadata.decoder.skip(ctx, br)?;
172 }
173
174 Ok(())
175 }
176
177 pub fn get_by_name(&self, path: &str, serializer: &Serializer) -> Option<&FieldValue> {
179 let key = serializer.resolve_field_key(path)?;
180 self.fields.get(&key)
181 }
182
183 pub fn get_i64(&self, key: Option<u64>) -> i64 {
194 key.and_then(|k| self.fields.get(&k))
195 .and_then(|v| match v {
196 FieldValue::U32(n) => Some(*n as i64),
197 FieldValue::U64(n) => Some(*n as i64),
198 FieldValue::I32(n) => Some(*n as i64),
199 FieldValue::I64(n) => Some(*n),
200 _ => None,
201 })
202 .unwrap_or(0)
203 }
204
205 pub fn get_u32(&self, key: Option<u64>) -> u32 {
207 key.and_then(|k| self.fields.get(&k))
208 .and_then(|v| match v {
209 FieldValue::U32(n) => Some(*n),
210 FieldValue::U64(n) => Some(*n as u32),
211 FieldValue::I32(n) => Some(*n as u32),
212 FieldValue::I64(n) => Some(*n as u32),
213 _ => None,
214 })
215 .unwrap_or(0)
216 }
217
218 pub fn get_f32(&self, key: Option<u64>) -> f32 {
220 key.and_then(|k| self.fields.get(&k))
221 .and_then(|v| match v {
222 FieldValue::F32(f) => Some(*f),
223 _ => None,
224 })
225 .unwrap_or(0.0)
226 }
227
228 pub fn get_bool(&self, key: Option<u64>) -> bool {
230 key.and_then(|k| self.fields.get(&k))
231 .and_then(|v| match v {
232 FieldValue::Bool(b) => Some(*b),
233 _ => None,
234 })
235 .unwrap_or(false)
236 }
237
238 pub fn get_qangle(&self, key: Option<u64>) -> [f32; 3] {
240 key.and_then(|k| self.fields.get(&k))
241 .and_then(|v| match v {
242 FieldValue::QAngle(a) => Some(*a),
243 _ => None,
244 })
245 .unwrap_or([0.0; 3])
246 }
247
248 pub fn world_position(
262 &self,
263 cell_keys: [Option<u64>; 3],
264 offset_keys: [Option<u64>; 3],
265 ) -> [f32; 3] {
266 let cell = [
267 self.get_i64(cell_keys[0]) as i32,
268 self.get_i64(cell_keys[1]) as i32,
269 self.get_i64(cell_keys[2]) as i32,
270 ];
271 let offset = [
272 self.get_f32(offset_keys[0]),
273 self.get_f32(offset_keys[1]),
274 self.get_f32(offset_keys[2]),
275 ];
276 [
277 crate::position::cell_to_world(cell[0], offset[0]),
278 crate::position::cell_to_world(cell[1], offset[1]),
279 crate::position::cell_to_world(cell[2], offset[2]),
280 ]
281 }
282
283 pub fn get_handle(&self, key: Option<u64>) -> Option<u32> {
289 key.and_then(|k| self.fields.get(&k)).and_then(|v| match v {
290 FieldValue::U32(n) => Some(*n),
291 FieldValue::U64(n) => Some(*n as u32),
292 FieldValue::I32(n) => Some(*n as u32),
293 FieldValue::I64(n) => Some(*n as u32),
294 _ => None,
295 })
296 }
297}
298
299#[derive(Default)]
301pub struct EntityContainer {
302 pub entities: FxHashMap<i32, Entity>,
303 skipped_entity_classes: FxHashMap<i32, i32>,
306}
307
308impl EntityContainer {
309 pub fn new() -> Self {
310 Self::default()
311 }
312
313 pub fn handle_packet_entities(
315 &mut self,
316 msg: CsvcMsgPacketEntities,
317 class_info: &ClassInfo,
318 serializers: &SerializerContainer,
319 string_tables: &StringTableContainer,
320 field_decode_ctx: &mut FieldDecodeContext,
321 fp_buf: &mut Vec<FieldPath>,
322 ) -> Result<()> {
323 let entity_data = msg.entity_data.unwrap_or_default();
324 let mut br = BitReader::new(&entity_data);
325
326 let mut entity_index: i32 = -1;
327
328 for _ in 0..msg.updated_entries.unwrap_or(0) {
329 entity_index += br.read_ubitvar()? as i32 + 1;
330
331 let dh = br.read_bits(2)? as u8;
333
334 match dh {
335 DELTA_CREATE => {
336 self.handle_create(
337 entity_index,
338 &mut br,
339 class_info,
340 serializers,
341 string_tables,
342 field_decode_ctx,
343 fp_buf,
344 )
345 .map_err(|e| Error::Parse {
346 context: format!("entity create #{}: {}", entity_index, e),
347 })?;
348 }
349 DELTA_UPDATE => {
350 self.handle_update(
351 entity_index,
352 &mut br,
353 class_info,
354 serializers,
355 field_decode_ctx,
356 fp_buf,
357 )
358 .map_err(|e| Error::Parse {
359 context: format!(
360 "entity update #{} (class: {:?}): {}",
361 entity_index,
362 self.entities.get(&entity_index).map(|e| &e.class_name),
363 e
364 ),
365 })?;
366 }
367 DELTA_DELETE | DELTA_LEAVE => {
368 self.entities.remove(&entity_index);
369 }
370 _ => {}
371 }
372 }
373
374 Ok(())
375 }
376
377 #[allow(clippy::too_many_arguments)]
380 pub fn handle_packet_entities_filtered(
381 &mut self,
382 msg: CsvcMsgPacketEntities,
383 class_info: &ClassInfo,
384 serializers: &SerializerContainer,
385 string_tables: &StringTableContainer,
386 field_decode_ctx: &mut FieldDecodeContext,
387 class_filter: &HashSet<&str>,
388 fp_buf: &mut Vec<FieldPath>,
389 ) -> Result<()> {
390 let entity_data = msg.entity_data.unwrap_or_default();
391 let mut br = BitReader::new(&entity_data);
392
393 let mut entity_index: i32 = -1;
394
395 for _ in 0..msg.updated_entries.unwrap_or(0) {
396 entity_index += br.read_ubitvar()? as i32 + 1;
397
398 let dh = br.read_bits(2)? as u8;
400
401 match dh {
402 DELTA_CREATE => {
403 self.handle_create_filtered(
404 entity_index,
405 &mut br,
406 class_info,
407 serializers,
408 string_tables,
409 field_decode_ctx,
410 class_filter,
411 fp_buf,
412 )?;
413 }
414 DELTA_UPDATE => {
415 self.handle_update_filtered(
416 entity_index,
417 &mut br,
418 class_info,
419 serializers,
420 field_decode_ctx,
421 class_filter,
422 fp_buf,
423 )?;
424 }
425 DELTA_DELETE | DELTA_LEAVE => {
426 self.entities.remove(&entity_index);
427 self.skipped_entity_classes.remove(&entity_index);
428 }
429 _ => {}
430 }
431 }
432
433 Ok(())
434 }
435
436 #[allow(clippy::too_many_arguments)]
437 fn handle_create(
438 &mut self,
439 index: i32,
440 br: &mut BitReader,
441 class_info: &ClassInfo,
442 serializers: &SerializerContainer,
443 string_tables: &StringTableContainer,
444 field_decode_ctx: &mut FieldDecodeContext,
445 fp_buf: &mut Vec<FieldPath>,
446 ) -> Result<()> {
447 let class_id = br.read_bits(class_info.bits)? as i32;
448 let _serial = br.read_bits(NUM_SERIAL_NUM_BITS as usize)?;
449 let _unknown = br.read_uvarint32()?;
450
451 let class_entry = class_info.by_id(class_id).ok_or_else(|| Error::Parse {
452 context: format!("unknown class_id {}", class_id),
453 })?;
454
455 let serializer =
456 serializers
457 .get(&class_entry.network_name)
458 .ok_or_else(|| Error::Parse {
459 context: format!("no serializer for {}", class_entry.network_name),
460 })?;
461
462 let mut entity = Entity::new(index, class_id, class_entry.network_name.clone());
463
464 if let Some(baseline_data) = string_tables.instance_baselines.get(&class_id) {
466 let mut baseline_br = BitReader::new(baseline_data);
467 entity
468 .apply_update(&mut baseline_br, serializer, field_decode_ctx, fp_buf)
469 .map_err(|err| Error::Parse {
470 context: format!(
471 "baseline for {} (class_id {}): {}",
472 class_entry.network_name, class_id, err
473 ),
474 })?;
475 }
476
477 entity
479 .apply_update(br, serializer, field_decode_ctx, fp_buf)
480 .map_err(|err| Error::Parse {
481 context: format!(
482 "create delta for {} (class_id {}): {}",
483 class_entry.network_name, class_id, err
484 ),
485 })?;
486 self.entities.insert(index, entity);
487
488 Ok(())
489 }
490
491 fn handle_update(
492 &mut self,
493 index: i32,
494 br: &mut BitReader,
495 _class_info: &ClassInfo,
496 serializers: &SerializerContainer,
497 field_decode_ctx: &mut FieldDecodeContext,
498 fp_buf: &mut Vec<FieldPath>,
499 ) -> Result<()> {
500 let entity = match self.entities.get_mut(&index) {
501 Some(e) => e,
502 None => {
503 return Err(Error::Parse {
504 context: format!("tried to update non-existent entity #{}", index),
505 });
506 }
507 };
508
509 let serializer = serializers
510 .get(&entity.class_name)
511 .ok_or_else(|| Error::Parse {
512 context: format!("no serializer for {}", entity.class_name),
513 })?;
514
515 entity.apply_update(br, serializer, field_decode_ctx, fp_buf)?;
516 Ok(())
517 }
518
519 #[allow(clippy::too_many_arguments)]
520 fn handle_create_filtered(
521 &mut self,
522 index: i32,
523 br: &mut BitReader,
524 class_info: &ClassInfo,
525 serializers: &SerializerContainer,
526 string_tables: &StringTableContainer,
527 field_decode_ctx: &mut FieldDecodeContext,
528 class_filter: &HashSet<&str>,
529 fp_buf: &mut Vec<FieldPath>,
530 ) -> Result<()> {
531 let class_id = br.read_bits(class_info.bits)? as i32;
532 let _serial = br.read_bits(NUM_SERIAL_NUM_BITS as usize)?;
533 let _unknown = br.read_uvarint32()?;
534
535 let class_entry = class_info.by_id(class_id).ok_or_else(|| Error::Parse {
536 context: format!("unknown class_id {}", class_id),
537 })?;
538
539 let serializer =
540 serializers
541 .get(&class_entry.network_name)
542 .ok_or_else(|| Error::Parse {
543 context: format!("no serializer for {}", class_entry.network_name),
544 })?;
545
546 if !class_filter.contains(class_entry.network_name.as_str()) {
548 self.skipped_entity_classes.insert(index, class_id);
551 Entity::skip_update(br, serializer, field_decode_ctx, fp_buf)?;
552 return Ok(());
553 }
554
555 let mut entity = Entity::new(index, class_id, class_entry.network_name.clone());
557
558 if let Some(baseline_data) = string_tables.instance_baselines.get(&class_id) {
559 let mut baseline_br = BitReader::new(baseline_data);
560 entity.apply_update(&mut baseline_br, serializer, field_decode_ctx, fp_buf)?;
561 }
562
563 entity.apply_update(br, serializer, field_decode_ctx, fp_buf)?;
564 self.entities.insert(index, entity);
565
566 Ok(())
567 }
568
569 #[allow(clippy::too_many_arguments)]
570 fn handle_update_filtered(
571 &mut self,
572 index: i32,
573 br: &mut BitReader,
574 class_info: &ClassInfo,
575 serializers: &SerializerContainer,
576 field_decode_ctx: &mut FieldDecodeContext,
577 _class_filter: &HashSet<&str>,
578 fp_buf: &mut Vec<FieldPath>,
579 ) -> Result<()> {
580 if let Some(entity) = self.entities.get_mut(&index) {
582 let serializer = serializers
583 .get(&entity.class_name)
584 .ok_or_else(|| Error::Parse {
585 context: format!("no serializer for {}", entity.class_name),
586 })?;
587
588 entity.apply_update(br, serializer, field_decode_ctx, fp_buf)?;
589 return Ok(());
590 }
591
592 if let Some(&class_id) = self.skipped_entity_classes.get(&index) {
594 let class_entry = class_info.by_id(class_id).ok_or_else(|| Error::Parse {
595 context: format!("unknown class_id {}", class_id),
596 })?;
597
598 let serializer =
599 serializers
600 .get(&class_entry.network_name)
601 .ok_or_else(|| Error::Parse {
602 context: format!("no serializer for {}", class_entry.network_name),
603 })?;
604
605 Entity::skip_update(br, serializer, field_decode_ctx, fp_buf)?;
607 }
608
609 Ok(())
612 }
613
614 pub fn get(&self, index: i32) -> Option<&Entity> {
616 self.entities.get(&index)
617 }
618
619 pub fn get_by_handle(&self, handle: u32) -> Option<&Entity> {
625 self.get((handle & ENTITY_HANDLE_INDEX_MASK) as i32)
626 }
627
628 pub fn iter(&self) -> impl Iterator<Item = (&i32, &Entity)> {
630 self.entities.iter()
631 }
632
633 pub fn len(&self) -> usize {
635 self.entities.len()
636 }
637
638 pub fn is_empty(&self) -> bool {
639 self.entities.is_empty()
640 }
641}
642
643#[cfg(test)]
644mod tests {
645 use super::*;
646
647 #[test]
648 fn container_new_is_empty() {
649 let c = EntityContainer::new();
650 assert!(c.is_empty());
651 assert_eq!(c.len(), 0);
652 assert!(c.get(0).is_none());
653 }
654
655 #[test]
656 fn entity_fields_insert_and_get() {
657 let mut e = Entity::new(1, 10, "TestClass".to_string());
658 e.fields.insert(42, FieldValue::I32(100));
659 assert!(matches!(e.fields.get(&42), Some(FieldValue::I32(100))));
660 }
661
662 #[test]
663 fn container_insert_and_iter() {
664 let mut c = EntityContainer::new();
665 let e = Entity::new(5, 10, "Hero".to_string());
666 c.entities.insert(5, e);
667 assert_eq!(c.len(), 1);
668 assert!(!c.is_empty());
669 assert!(c.get(5).is_some());
670 assert_eq!(c.get(5).unwrap().class_name, "Hero");
671 }
672
673 #[test]
674 fn container_iter_yields_entries() {
675 let mut c = EntityContainer::new();
676 c.entities.insert(1, Entity::new(1, 1, "A".to_string()));
677 c.entities.insert(2, Entity::new(2, 2, "B".to_string()));
678 let keys: Vec<i32> = c.iter().map(|(&k, _)| k).collect();
679 assert_eq!(keys.len(), 2);
680 }
681
682 #[test]
683 fn entity_basic_fields() {
684 let e = Entity::new(7, 42, "NPC".to_string());
685 assert_eq!(e.index, 7);
686 assert_eq!(e.class_id, 42);
687 assert_eq!(e.class_name, "NPC");
688 assert_eq!(e.serial, 0);
689 assert!(e.fields.is_empty());
690 }
691}