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 updated: Vec<i32>,
312}
313
314impl EntityContainer {
315 pub fn new() -> Self {
316 Self::default()
317 }
318
319 pub fn handle_packet_entities(
321 &mut self,
322 msg: CsvcMsgPacketEntities,
323 class_info: &ClassInfo,
324 serializers: &SerializerContainer,
325 string_tables: &StringTableContainer,
326 field_decode_ctx: &mut FieldDecodeContext,
327 fp_buf: &mut Vec<FieldPath>,
328 ) -> Result<()> {
329 let entity_data = msg.entity_data.unwrap_or_default();
330 let mut br = BitReader::new(&entity_data);
331
332 let mut entity_index: i32 = -1;
333
334 for _ in 0..msg.updated_entries.unwrap_or(0) {
335 entity_index += br.read_ubitvar()? as i32 + 1;
336
337 let dh = br.read_bits(2)? as u8;
339
340 match dh {
341 DELTA_CREATE => {
342 self.handle_create(
343 entity_index,
344 &mut br,
345 class_info,
346 serializers,
347 string_tables,
348 field_decode_ctx,
349 fp_buf,
350 )
351 .map_err(|e| Error::Parse {
352 context: format!("entity create #{}: {}", entity_index, e),
353 })?;
354 self.updated.push(entity_index);
355 }
356 DELTA_UPDATE => {
357 self.handle_update(
358 entity_index,
359 &mut br,
360 class_info,
361 serializers,
362 field_decode_ctx,
363 fp_buf,
364 )
365 .map_err(|e| Error::Parse {
366 context: format!(
367 "entity update #{} (class: {:?}): {}",
368 entity_index,
369 self.entities.get(&entity_index).map(|e| &e.class_name),
370 e
371 ),
372 })?;
373 self.updated.push(entity_index);
374 }
375 DELTA_DELETE | DELTA_LEAVE => {
376 self.entities.remove(&entity_index);
377 }
378 _ => {}
379 }
380 }
381
382 Ok(())
383 }
384
385 #[allow(clippy::too_many_arguments)]
388 pub fn handle_packet_entities_filtered(
389 &mut self,
390 msg: CsvcMsgPacketEntities,
391 class_info: &ClassInfo,
392 serializers: &SerializerContainer,
393 string_tables: &StringTableContainer,
394 field_decode_ctx: &mut FieldDecodeContext,
395 class_filter: &HashSet<&str>,
396 fp_buf: &mut Vec<FieldPath>,
397 ) -> Result<()> {
398 let entity_data = msg.entity_data.unwrap_or_default();
399 let mut br = BitReader::new(&entity_data);
400
401 let mut entity_index: i32 = -1;
402
403 for _ in 0..msg.updated_entries.unwrap_or(0) {
404 entity_index += br.read_ubitvar()? as i32 + 1;
405
406 let dh = br.read_bits(2)? as u8;
408
409 match dh {
410 DELTA_CREATE => {
411 if self.handle_create_filtered(
412 entity_index,
413 &mut br,
414 class_info,
415 serializers,
416 string_tables,
417 field_decode_ctx,
418 class_filter,
419 fp_buf,
420 )? {
421 self.updated.push(entity_index);
422 }
423 }
424 DELTA_UPDATE => {
425 if self.handle_update_filtered(
426 entity_index,
427 &mut br,
428 class_info,
429 serializers,
430 field_decode_ctx,
431 class_filter,
432 fp_buf,
433 )? {
434 self.updated.push(entity_index);
435 }
436 }
437 DELTA_DELETE | DELTA_LEAVE => {
438 self.entities.remove(&entity_index);
439 self.skipped_entity_classes.remove(&entity_index);
440 }
441 _ => {}
442 }
443 }
444
445 Ok(())
446 }
447
448 #[allow(clippy::too_many_arguments)]
449 fn handle_create(
450 &mut self,
451 index: i32,
452 br: &mut BitReader,
453 class_info: &ClassInfo,
454 serializers: &SerializerContainer,
455 string_tables: &StringTableContainer,
456 field_decode_ctx: &mut FieldDecodeContext,
457 fp_buf: &mut Vec<FieldPath>,
458 ) -> Result<()> {
459 let class_id = br.read_bits(class_info.bits)? as i32;
460 let _serial = br.read_bits(NUM_SERIAL_NUM_BITS as usize)?;
461 let _unknown = br.read_uvarint32()?;
462
463 let class_entry = class_info.by_id(class_id).ok_or_else(|| Error::Parse {
464 context: format!("unknown class_id {}", class_id),
465 })?;
466
467 let serializer =
468 serializers
469 .get(&class_entry.network_name)
470 .ok_or_else(|| Error::Parse {
471 context: format!("no serializer for {}", class_entry.network_name),
472 })?;
473
474 let mut entity = Entity::new(index, class_id, class_entry.network_name.clone());
475 entity.fields.reserve(serializer.fields.len());
479
480 if let Some(baseline_data) = string_tables.instance_baselines.get(&class_id) {
482 let mut baseline_br = BitReader::new(baseline_data);
483 entity
484 .apply_update(&mut baseline_br, serializer, field_decode_ctx, fp_buf)
485 .map_err(|err| Error::Parse {
486 context: format!(
487 "baseline for {} (class_id {}): {}",
488 class_entry.network_name, class_id, err
489 ),
490 })?;
491 }
492
493 entity
495 .apply_update(br, serializer, field_decode_ctx, fp_buf)
496 .map_err(|err| Error::Parse {
497 context: format!(
498 "create delta for {} (class_id {}): {}",
499 class_entry.network_name, class_id, err
500 ),
501 })?;
502 self.entities.insert(index, entity);
503
504 Ok(())
505 }
506
507 fn handle_update(
508 &mut self,
509 index: i32,
510 br: &mut BitReader,
511 _class_info: &ClassInfo,
512 serializers: &SerializerContainer,
513 field_decode_ctx: &mut FieldDecodeContext,
514 fp_buf: &mut Vec<FieldPath>,
515 ) -> Result<()> {
516 let entity = match self.entities.get_mut(&index) {
517 Some(e) => e,
518 None => {
519 return Err(Error::Parse {
520 context: format!("tried to update non-existent entity #{}", index),
521 });
522 }
523 };
524
525 let serializer = serializers
526 .get(&entity.class_name)
527 .ok_or_else(|| Error::Parse {
528 context: format!("no serializer for {}", entity.class_name),
529 })?;
530
531 entity.apply_update(br, serializer, field_decode_ctx, fp_buf)?;
532 Ok(())
533 }
534
535 #[allow(clippy::too_many_arguments)]
537 fn handle_create_filtered(
538 &mut self,
539 index: i32,
540 br: &mut BitReader,
541 class_info: &ClassInfo,
542 serializers: &SerializerContainer,
543 string_tables: &StringTableContainer,
544 field_decode_ctx: &mut FieldDecodeContext,
545 class_filter: &HashSet<&str>,
546 fp_buf: &mut Vec<FieldPath>,
547 ) -> Result<bool> {
548 let class_id = br.read_bits(class_info.bits)? as i32;
549 let _serial = br.read_bits(NUM_SERIAL_NUM_BITS as usize)?;
550 let _unknown = br.read_uvarint32()?;
551
552 let class_entry = class_info.by_id(class_id).ok_or_else(|| Error::Parse {
553 context: format!("unknown class_id {}", class_id),
554 })?;
555
556 let serializer =
557 serializers
558 .get(&class_entry.network_name)
559 .ok_or_else(|| Error::Parse {
560 context: format!("no serializer for {}", class_entry.network_name),
561 })?;
562
563 if !class_filter.contains(class_entry.network_name.as_str()) {
565 self.skipped_entity_classes.insert(index, class_id);
568 Entity::skip_update(br, serializer, field_decode_ctx, fp_buf)?;
569 return Ok(false);
570 }
571
572 let mut entity = Entity::new(index, class_id, class_entry.network_name.clone());
574 entity.fields.reserve(serializer.fields.len());
578
579 if let Some(baseline_data) = string_tables.instance_baselines.get(&class_id) {
580 let mut baseline_br = BitReader::new(baseline_data);
581 entity.apply_update(&mut baseline_br, serializer, field_decode_ctx, fp_buf)?;
582 }
583
584 entity.apply_update(br, serializer, field_decode_ctx, fp_buf)?;
585 self.entities.insert(index, entity);
586
587 Ok(true)
588 }
589
590 #[allow(clippy::too_many_arguments)]
592 fn handle_update_filtered(
593 &mut self,
594 index: i32,
595 br: &mut BitReader,
596 class_info: &ClassInfo,
597 serializers: &SerializerContainer,
598 field_decode_ctx: &mut FieldDecodeContext,
599 _class_filter: &HashSet<&str>,
600 fp_buf: &mut Vec<FieldPath>,
601 ) -> Result<bool> {
602 if let Some(entity) = self.entities.get_mut(&index) {
604 let serializer = serializers
605 .get(&entity.class_name)
606 .ok_or_else(|| Error::Parse {
607 context: format!("no serializer for {}", entity.class_name),
608 })?;
609
610 entity.apply_update(br, serializer, field_decode_ctx, fp_buf)?;
611 return Ok(true);
612 }
613
614 if let Some(&class_id) = self.skipped_entity_classes.get(&index) {
616 let class_entry = class_info.by_id(class_id).ok_or_else(|| Error::Parse {
617 context: format!("unknown class_id {}", class_id),
618 })?;
619
620 let serializer =
621 serializers
622 .get(&class_entry.network_name)
623 .ok_or_else(|| Error::Parse {
624 context: format!("no serializer for {}", class_entry.network_name),
625 })?;
626
627 Entity::skip_update(br, serializer, field_decode_ctx, fp_buf)?;
629 }
630
631 Ok(false)
634 }
635
636 pub fn get(&self, index: i32) -> Option<&Entity> {
638 self.entities.get(&index)
639 }
640
641 pub fn get_by_handle(&self, handle: u32) -> Option<&Entity> {
647 self.get((handle & ENTITY_HANDLE_INDEX_MASK) as i32)
648 }
649
650 pub fn iter(&self) -> impl Iterator<Item = (&i32, &Entity)> {
652 self.entities.iter()
653 }
654
655 pub fn updated_indices(&self) -> &[i32] {
667 &self.updated
668 }
669
670 pub fn clear_updated(&mut self) {
672 self.updated.clear();
673 }
674
675 pub fn len(&self) -> usize {
677 self.entities.len()
678 }
679
680 pub fn is_empty(&self) -> bool {
681 self.entities.is_empty()
682 }
683}
684
685#[cfg(test)]
686mod tests {
687 use super::*;
688
689 #[test]
690 fn container_new_is_empty() {
691 let c = EntityContainer::new();
692 assert!(c.is_empty());
693 assert_eq!(c.len(), 0);
694 assert!(c.get(0).is_none());
695 }
696
697 #[test]
698 fn entity_fields_insert_and_get() {
699 let mut e = Entity::new(1, 10, "TestClass".to_string());
700 e.fields.insert(42, FieldValue::I32(100));
701 assert!(matches!(e.fields.get(&42), Some(FieldValue::I32(100))));
702 }
703
704 #[test]
705 fn container_insert_and_iter() {
706 let mut c = EntityContainer::new();
707 let e = Entity::new(5, 10, "Hero".to_string());
708 c.entities.insert(5, e);
709 assert_eq!(c.len(), 1);
710 assert!(!c.is_empty());
711 assert!(c.get(5).is_some());
712 assert_eq!(c.get(5).unwrap().class_name, "Hero");
713 }
714
715 #[test]
716 fn container_iter_yields_entries() {
717 let mut c = EntityContainer::new();
718 c.entities.insert(1, Entity::new(1, 1, "A".to_string()));
719 c.entities.insert(2, Entity::new(2, 2, "B".to_string()));
720 let keys: Vec<i32> = c.iter().map(|(&k, _)| k).collect();
721 assert_eq!(keys.len(), 2);
722 }
723
724 #[test]
725 fn entity_basic_fields() {
726 let e = Entity::new(7, 42, "NPC".to_string());
727 assert_eq!(e.index, 7);
728 assert_eq!(e.class_id, 42);
729 assert_eq!(e.class_name, "NPC");
730 assert_eq!(e.serial, 0);
731 assert!(e.fields.is_empty());
732 }
733}