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;
21const NUM_ENT_ENTRY_BITS: u32 = MAX_EDICT_BITS + 1;
22const NUM_SERIAL_NUM_BITS: u32 = 32 - NUM_ENT_ENTRY_BITS;
23
24const DELTA_UPDATE: u8 = 0b00;
26const DELTA_CREATE: u8 = 0b10;
27const DELTA_LEAVE: u8 = 0b01;
28const DELTA_DELETE: u8 = 0b11;
29
30#[derive(Debug, Clone)]
32pub struct Entity {
33 pub index: i32,
35 pub serial: u32,
37 pub class_id: i32,
39 pub class_name: String,
41 pub fields: FxHashMap<u64, FieldValue>,
43}
44
45impl Entity {
46 fn new(index: i32, class_id: i32, class_name: String) -> Self {
47 Self {
48 index,
49 serial: 0,
50 class_id,
51 class_name,
52 fields: FxHashMap::default(),
53 }
54 }
55
56 #[allow(clippy::needless_range_loop)]
58 fn apply_update(
59 &mut self,
60 br: &mut BitReader,
61 serializer: &Serializer,
62 ctx: &mut FieldDecodeContext,
63 fp_buf: &mut Vec<FieldPath>,
64 ) -> Result<()> {
65 field_path::read_field_paths(br, fp_buf)?;
66
67 for fp_idx in 0..fp_buf.len() {
68 let fp_last = fp_buf[fp_idx].last;
70 let mut field = &serializer.fields[fp_buf[fp_idx].get(0)];
71
72 for i in 1..=fp_last {
73 let idx = fp_buf[fp_idx].get(i);
74 if field.is_dynamic_array() {
75 if let Some(ref fs) = field.field_serializer {
76 field = &fs.fields[0];
77 }
78 } else if let Some(ref fs) = field.field_serializer {
79 field = &fs.fields[idx];
80 } else {
81 break;
82 }
83 }
84
85 let key = fp_buf[fp_idx].pack();
86 let value = field
87 .metadata
88 .decoder
89 .decode(ctx, br)
90 .map_err(|e| Error::Parse {
91 context: format!(
92 "field #{} key={:#x} (type: {}, decoder: {:?}, pos: {}, remaining: {}): {}",
93 fp_idx,
94 key,
95 field.var_type,
96 field.metadata.decoder,
97 br.position(),
98 br.bits_remaining(),
99 e
100 ),
101 })?;
102 self.fields.insert(key, value);
103 }
104
105 Ok(())
106 }
107
108 #[allow(clippy::needless_range_loop)]
111 fn skip_update(
112 br: &mut BitReader,
113 serializer: &Serializer,
114 ctx: &mut FieldDecodeContext,
115 fp_buf: &mut Vec<FieldPath>,
116 ) -> Result<()> {
117 field_path::read_field_paths(br, fp_buf)?;
118
119 for fp_idx in 0..fp_buf.len() {
120 let fp_last = fp_buf[fp_idx].last;
122 let mut field = &serializer.fields[fp_buf[fp_idx].get(0)];
123
124 for i in 1..=fp_last {
125 let idx = fp_buf[fp_idx].get(i);
126 if field.is_dynamic_array() {
127 if let Some(ref fs) = field.field_serializer {
128 field = &fs.fields[0];
129 }
130 } else if let Some(ref fs) = field.field_serializer {
131 field = &fs.fields[idx];
132 } else {
133 break;
134 }
135 }
136
137 field.metadata.decoder.skip(ctx, br)?;
139 }
140
141 Ok(())
142 }
143
144 pub fn get_by_name(&self, path: &str, serializer: &Serializer) -> Option<&FieldValue> {
146 let key = serializer.resolve_field_key(path)?;
147 self.fields.get(&key)
148 }
149}
150
151#[derive(Default)]
153pub struct EntityContainer {
154 pub entities: FxHashMap<i32, Entity>,
155 skipped_entity_classes: FxHashMap<i32, i32>,
158}
159
160impl EntityContainer {
161 pub fn new() -> Self {
162 Self::default()
163 }
164
165 pub fn handle_packet_entities(
167 &mut self,
168 msg: CsvcMsgPacketEntities,
169 class_info: &ClassInfo,
170 serializers: &SerializerContainer,
171 string_tables: &StringTableContainer,
172 field_decode_ctx: &mut FieldDecodeContext,
173 fp_buf: &mut Vec<FieldPath>,
174 ) -> Result<()> {
175 let entity_data = msg.entity_data.unwrap_or_default();
176 let mut br = BitReader::new(&entity_data);
177
178 let mut entity_index: i32 = -1;
179
180 for _ in 0..msg.updated_entries.unwrap_or(0) {
181 entity_index += br.read_ubitvar()? as i32 + 1;
182
183 let dh = br.read_bits(2)? as u8;
185
186 match dh {
187 DELTA_CREATE => {
188 self.handle_create(
189 entity_index,
190 &mut br,
191 class_info,
192 serializers,
193 string_tables,
194 field_decode_ctx,
195 fp_buf,
196 )
197 .map_err(|e| Error::Parse {
198 context: format!("entity create #{}: {}", entity_index, e),
199 })?;
200 }
201 DELTA_UPDATE => {
202 self.handle_update(
203 entity_index,
204 &mut br,
205 class_info,
206 serializers,
207 field_decode_ctx,
208 fp_buf,
209 )
210 .map_err(|e| Error::Parse {
211 context: format!(
212 "entity update #{} (class: {:?}): {}",
213 entity_index,
214 self.entities.get(&entity_index).map(|e| &e.class_name),
215 e
216 ),
217 })?;
218 }
219 DELTA_DELETE | DELTA_LEAVE => {
220 self.entities.remove(&entity_index);
221 }
222 _ => {}
223 }
224 }
225
226 Ok(())
227 }
228
229 #[allow(clippy::too_many_arguments)]
232 pub fn handle_packet_entities_filtered(
233 &mut self,
234 msg: CsvcMsgPacketEntities,
235 class_info: &ClassInfo,
236 serializers: &SerializerContainer,
237 string_tables: &StringTableContainer,
238 field_decode_ctx: &mut FieldDecodeContext,
239 class_filter: &HashSet<&str>,
240 fp_buf: &mut Vec<FieldPath>,
241 ) -> Result<()> {
242 let entity_data = msg.entity_data.unwrap_or_default();
243 let mut br = BitReader::new(&entity_data);
244
245 let mut entity_index: i32 = -1;
246
247 for _ in 0..msg.updated_entries.unwrap_or(0) {
248 entity_index += br.read_ubitvar()? as i32 + 1;
249
250 let dh = br.read_bits(2)? as u8;
252
253 match dh {
254 DELTA_CREATE => {
255 self.handle_create_filtered(
256 entity_index,
257 &mut br,
258 class_info,
259 serializers,
260 string_tables,
261 field_decode_ctx,
262 class_filter,
263 fp_buf,
264 )?;
265 }
266 DELTA_UPDATE => {
267 self.handle_update_filtered(
268 entity_index,
269 &mut br,
270 class_info,
271 serializers,
272 field_decode_ctx,
273 class_filter,
274 fp_buf,
275 )?;
276 }
277 DELTA_DELETE | DELTA_LEAVE => {
278 self.entities.remove(&entity_index);
279 self.skipped_entity_classes.remove(&entity_index);
280 }
281 _ => {}
282 }
283 }
284
285 Ok(())
286 }
287
288 #[allow(clippy::too_many_arguments)]
289 fn handle_create(
290 &mut self,
291 index: i32,
292 br: &mut BitReader,
293 class_info: &ClassInfo,
294 serializers: &SerializerContainer,
295 string_tables: &StringTableContainer,
296 field_decode_ctx: &mut FieldDecodeContext,
297 fp_buf: &mut Vec<FieldPath>,
298 ) -> Result<()> {
299 let class_id = br.read_bits(class_info.bits)? as i32;
300 let _serial = br.read_bits(NUM_SERIAL_NUM_BITS as usize)?;
301 let _unknown = br.read_uvarint32()?;
302
303 let class_entry = class_info.by_id(class_id).ok_or_else(|| Error::Parse {
304 context: format!("unknown class_id {}", class_id),
305 })?;
306
307 let serializer =
308 serializers
309 .get(&class_entry.network_name)
310 .ok_or_else(|| Error::Parse {
311 context: format!("no serializer for {}", class_entry.network_name),
312 })?;
313
314 let mut entity = Entity::new(index, class_id, class_entry.network_name.clone());
315
316 if let Some(baseline_data) = string_tables.instance_baselines.get(&class_id) {
318 let mut baseline_br = BitReader::new(baseline_data);
319 entity
320 .apply_update(&mut baseline_br, serializer, field_decode_ctx, fp_buf)
321 .map_err(|err| Error::Parse {
322 context: format!(
323 "baseline for {} (class_id {}): {}",
324 class_entry.network_name, class_id, err
325 ),
326 })?;
327 }
328
329 entity
331 .apply_update(br, serializer, field_decode_ctx, fp_buf)
332 .map_err(|err| Error::Parse {
333 context: format!(
334 "create delta for {} (class_id {}): {}",
335 class_entry.network_name, class_id, err
336 ),
337 })?;
338 self.entities.insert(index, entity);
339
340 Ok(())
341 }
342
343 fn handle_update(
344 &mut self,
345 index: i32,
346 br: &mut BitReader,
347 _class_info: &ClassInfo,
348 serializers: &SerializerContainer,
349 field_decode_ctx: &mut FieldDecodeContext,
350 fp_buf: &mut Vec<FieldPath>,
351 ) -> Result<()> {
352 let entity = match self.entities.get_mut(&index) {
353 Some(e) => e,
354 None => {
355 return Err(Error::Parse {
356 context: format!("tried to update non-existent entity #{}", index),
357 });
358 }
359 };
360
361 let serializer = serializers
362 .get(&entity.class_name)
363 .ok_or_else(|| Error::Parse {
364 context: format!("no serializer for {}", entity.class_name),
365 })?;
366
367 entity.apply_update(br, serializer, field_decode_ctx, fp_buf)?;
368 Ok(())
369 }
370
371 #[allow(clippy::too_many_arguments)]
372 fn handle_create_filtered(
373 &mut self,
374 index: i32,
375 br: &mut BitReader,
376 class_info: &ClassInfo,
377 serializers: &SerializerContainer,
378 string_tables: &StringTableContainer,
379 field_decode_ctx: &mut FieldDecodeContext,
380 class_filter: &HashSet<&str>,
381 fp_buf: &mut Vec<FieldPath>,
382 ) -> Result<()> {
383 let class_id = br.read_bits(class_info.bits)? as i32;
384 let _serial = br.read_bits(NUM_SERIAL_NUM_BITS as usize)?;
385 let _unknown = br.read_uvarint32()?;
386
387 let class_entry = class_info.by_id(class_id).ok_or_else(|| Error::Parse {
388 context: format!("unknown class_id {}", class_id),
389 })?;
390
391 let serializer =
392 serializers
393 .get(&class_entry.network_name)
394 .ok_or_else(|| Error::Parse {
395 context: format!("no serializer for {}", class_entry.network_name),
396 })?;
397
398 if !class_filter.contains(class_entry.network_name.as_str()) {
400 self.skipped_entity_classes.insert(index, class_id);
403 Entity::skip_update(br, serializer, field_decode_ctx, fp_buf)?;
404 return Ok(());
405 }
406
407 let mut entity = Entity::new(index, class_id, class_entry.network_name.clone());
409
410 if let Some(baseline_data) = string_tables.instance_baselines.get(&class_id) {
411 let mut baseline_br = BitReader::new(baseline_data);
412 entity.apply_update(&mut baseline_br, serializer, field_decode_ctx, fp_buf)?;
413 }
414
415 entity.apply_update(br, serializer, field_decode_ctx, fp_buf)?;
416 self.entities.insert(index, entity);
417
418 Ok(())
419 }
420
421 #[allow(clippy::too_many_arguments)]
422 fn handle_update_filtered(
423 &mut self,
424 index: i32,
425 br: &mut BitReader,
426 class_info: &ClassInfo,
427 serializers: &SerializerContainer,
428 field_decode_ctx: &mut FieldDecodeContext,
429 _class_filter: &HashSet<&str>,
430 fp_buf: &mut Vec<FieldPath>,
431 ) -> Result<()> {
432 if let Some(entity) = self.entities.get_mut(&index) {
434 let serializer = serializers
435 .get(&entity.class_name)
436 .ok_or_else(|| Error::Parse {
437 context: format!("no serializer for {}", entity.class_name),
438 })?;
439
440 entity.apply_update(br, serializer, field_decode_ctx, fp_buf)?;
441 return Ok(());
442 }
443
444 if let Some(&class_id) = self.skipped_entity_classes.get(&index) {
446 let class_entry = class_info.by_id(class_id).ok_or_else(|| Error::Parse {
447 context: format!("unknown class_id {}", class_id),
448 })?;
449
450 let serializer =
451 serializers
452 .get(&class_entry.network_name)
453 .ok_or_else(|| Error::Parse {
454 context: format!("no serializer for {}", class_entry.network_name),
455 })?;
456
457 Entity::skip_update(br, serializer, field_decode_ctx, fp_buf)?;
459 }
460
461 Ok(())
464 }
465
466 pub fn get(&self, index: i32) -> Option<&Entity> {
468 self.entities.get(&index)
469 }
470
471 pub fn iter(&self) -> impl Iterator<Item = (&i32, &Entity)> {
473 self.entities.iter()
474 }
475
476 pub fn len(&self) -> usize {
478 self.entities.len()
479 }
480
481 pub fn is_empty(&self) -> bool {
482 self.entities.is_empty()
483 }
484}
485
486#[cfg(test)]
487mod tests {
488 use super::*;
489
490 #[test]
491 fn container_new_is_empty() {
492 let c = EntityContainer::new();
493 assert!(c.is_empty());
494 assert_eq!(c.len(), 0);
495 assert!(c.get(0).is_none());
496 }
497
498 #[test]
499 fn entity_fields_insert_and_get() {
500 let mut e = Entity::new(1, 10, "TestClass".to_string());
501 e.fields.insert(42, FieldValue::I32(100));
502 assert!(matches!(e.fields.get(&42), Some(FieldValue::I32(100))));
503 }
504
505 #[test]
506 fn container_insert_and_iter() {
507 let mut c = EntityContainer::new();
508 let e = Entity::new(5, 10, "Hero".to_string());
509 c.entities.insert(5, e);
510 assert_eq!(c.len(), 1);
511 assert!(!c.is_empty());
512 assert!(c.get(5).is_some());
513 assert_eq!(c.get(5).unwrap().class_name, "Hero");
514 }
515
516 #[test]
517 fn container_iter_yields_entries() {
518 let mut c = EntityContainer::new();
519 c.entities.insert(1, Entity::new(1, 1, "A".to_string()));
520 c.entities.insert(2, Entity::new(2, 2, "B".to_string()));
521 let keys: Vec<i32> = c.iter().map(|(&k, _)| k).collect();
522 assert_eq!(keys.len(), 2);
523 }
524
525 #[test]
526 fn entity_basic_fields() {
527 let e = Entity::new(7, 42, "NPC".to_string());
528 assert_eq!(e.index, 7);
529 assert_eq!(e.class_id, 42);
530 assert_eq!(e.class_name, "NPC");
531 assert_eq!(e.serial, 0);
532 assert!(e.fields.is_empty());
533 }
534}