1use crate::class::{Class, ClassError, Classes};
2use crate::combat_log::{CombatLogEntry, CombatLogError};
3use crate::decoder::Decoder;
4use crate::entity::{Entities, Entity, EntityError, EntityEvents};
5use crate::field::{Encoder, Field, FieldModel, FieldProperties, FieldState, FieldType};
6use crate::field_reader::FieldReader;
7use crate::field_value::FieldValueError;
8use crate::proto::*;
9use crate::reader::Reader;
10use crate::serializer::Serializer;
11use crate::string_table::{StringTable, StringTableError, StringTableRow, StringTables};
12use crate::ObserverResult;
13use hashbrown::HashMap;
14use prettytable::{row, Table};
15use std::cell::RefCell;
16use std::cmp::min;
17use std::collections::VecDeque;
18use std::fmt::{Display, Formatter};
19use std::mem;
20use std::rc::Rc;
21
22macro_rules! try_observers {
23 ($self:ident, $method:ident ( $($arg:expr),* )) => {
24 $self.observers
25 .iter()
26 .try_for_each(|obs| obs.borrow_mut().$method($($arg),*))
27 };
28}
29
30#[derive(thiserror::Error, Debug)]
32pub enum ParserError {
33 #[error(transparent)]
34 ProtobufDecode(#[from] prost::DecodeError),
35
36 #[error(transparent)]
37 SnapDecompress(#[from] snap::Error),
38
39 #[error(transparent)]
40 StringTable(#[from] StringTableError),
41
42 #[error(transparent)]
43 Class(#[from] ClassError),
44
45 #[error(transparent)]
46 Entity(#[from] EntityError),
47
48 #[error(transparent)]
49 FieldValue(#[from] FieldValueError),
50
51 #[error(transparent)]
52 CombatLog(#[from] CombatLogError),
53
54 #[error(transparent)]
55 ObserverError(#[from] anyhow::Error),
56
57 #[error("Wrong CDemoFileInfo offset")]
58 ReplayEncodingError,
59
60 #[error("Supports only Source 2 replays")]
61 WrongMagic,
62}
63
64pub struct Parser<'a> {
65 reader: Reader<'a>,
66 field_reader: FieldReader,
67 observers: Vec<Rc<RefCell<dyn Observer + 'a>>>,
68
69 combat_log: VecDeque<CMsgDotaCombatLogEntry>,
70
71 prologue_completed: bool,
72 skip_deltas: bool,
73
74 replay_info: CDemoFileInfo,
75 last_tick: u32,
76 context: Context,
77}
78
79#[derive(Default)]
80pub(crate) struct Baselines {
81 baselines: HashMap<i32, Rc<Vec<u8>>>,
82 states: HashMap<i32, FieldState>,
83}
84
85impl Baselines {
86 pub(crate) fn add_baseline(&mut self, id: i32, baseline: Rc<Vec<u8>>) {
87 self.baselines.insert(id, baseline);
88 }
89}
90
91pub struct Context {
93 pub(crate) classes: Classes,
94 pub(crate) entities: Entities,
95 pub(crate) string_tables: StringTables,
96
97 pub(crate) tick: u32,
98 pub(crate) previous_tick: u32,
99
100 pub(crate) net_tick: u32,
101 pub(crate) game_build: u32,
102
103 baselines: Baselines,
104 serializers: HashMap<Box<str>, Rc<Serializer>>,
105 last_full_packet_tick: u32,
106}
107
108impl Default for Context {
109 fn default() -> Self {
110 Context {
111 classes: Classes::default(),
112 entities: Entities::default(),
113 string_tables: StringTables::default(),
114 tick: u32::MAX,
115 previous_tick: u32::MAX,
116 net_tick: u32::MAX,
117 game_build: 0,
118 baselines: Baselines::default(),
119 serializers: HashMap::default(),
120 last_full_packet_tick: u32::MAX,
121 }
122 }
123}
124
125impl Context {
126 pub fn classes(&self) -> &Classes {
127 &self.classes
128 }
129
130 pub fn entities(&self) -> &Entities {
131 &self.entities
132 }
133
134 pub fn string_tables(&self) -> &StringTables {
135 &self.string_tables
136 }
137
138 pub fn tick(&self) -> u32 {
139 self.tick
140 }
141
142 pub fn net_tick(&self) -> u32 {
143 self.net_tick
144 }
145
146 pub fn game_build(&self) -> u32 {
147 self.game_build
148 }
149}
150
151impl Display for Context {
152 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
153 let mut table = Table::new();
154 table.add_row(row!["Classes", self.classes.classes_vec.len()]);
155 table.add_row(row!["Entities", self.entities.entities_vec.len()]);
156 table.add_row(row!["String Tables", self.string_tables.tables.len()]);
157 table.add_row(row!["Tick", self.tick]);
158 table.add_row(row!["Net Tick", self.net_tick]);
159 table.add_row(row!["Game Build", format!("{:?}", self.game_build)]);
160 write!(f, "{}", table)
161 }
162}
163
164pub(crate) struct OuterMessage {
165 pub(crate) msg_type: EDemoCommands,
166 pub(crate) tick: u32,
167 pub(crate) buf: Vec<u8>,
168}
169
170impl<'a> Parser<'a> {
171 pub fn new(replay: &'a [u8]) -> Result<Self, ParserError> {
173 let mut reader = Reader::new(replay);
174
175 if replay.len() < 16 || reader.read_bytes(8) != b"PBDEMS2\0" {
176 return Err(ParserError::WrongMagic);
177 };
178
179 reader.read_bytes(8);
180
181 let replay_info = reader.read_replay_info()?;
182 let last_tick = replay_info.playback_ticks() as u32;
183
184 Ok(Parser {
185 reader,
186 field_reader: FieldReader::default(),
187 observers: Vec::default(),
188 combat_log: VecDeque::default(),
189 prologue_completed: false,
190 skip_deltas: false,
191
192 replay_info,
193 last_tick,
194
195 context: Context::default(),
196 })
197 }
198
199 pub fn register_observer<T>(&mut self) -> Rc<RefCell<T>>
202 where
203 T: Observer + Default + 'a,
204 {
205 let rc = Rc::new(RefCell::new(T::default()));
206 self.observers.push(rc.clone());
207 rc.clone()
208 }
209
210 fn prologue(&mut self) -> Result<(), ParserError> {
211 if self.prologue_completed && self.context.tick != u32::MAX {
212 return Ok(());
213 }
214
215 while let Some(message) = self.reader.read_next_message()? {
216 if self.prologue_completed
217 && (message.msg_type == EDemoCommands::DemSendTables
218 || message.msg_type == EDemoCommands::DemClassInfo)
219 {
220 continue;
221 }
222
223 self.on_demo_command(message.msg_type, message.buf.as_slice())?;
224
225 if message.msg_type == EDemoCommands::DemSyncTick {
226 self.prologue_completed = true;
227 break;
228 }
229 }
230
231 Ok(())
232 }
233
234 pub fn context(&self) -> &Context {
235 &self.context
236 }
237
238 pub fn replay_info(&self) -> &CDemoFileInfo {
239 &self.replay_info
240 }
241
242 pub fn run_to_end(&mut self) -> Result<(), ParserError> {
244 self.prologue()?;
245
246 while let Some(message) = self.reader.read_next_message()? {
247 self.on_tick_start(message.tick)?;
248 self.on_demo_command(message.msg_type, message.buf.as_slice())?;
249 self.on_tick_end(message.tick)?;
250 }
251
252 try_observers!(self, epilogue(&self.context))?;
253 Ok(())
254 }
255
256 pub fn jump_to_tick(&mut self, mut target_tick: u32) -> Result<(), ParserError> {
259 target_tick = min(target_tick, self.last_tick);
260
261 if target_tick < self.context.tick {
262 self.context.last_full_packet_tick = u32::MAX;
263 self.context.tick = u32::MAX;
264 self.context.net_tick = u32::MAX;
265 self.reader.reset_to(16);
266
267 self.context.entities.entities_vec = vec![None; 8192];
268
269 self.context.string_tables.tables.clear();
270 self.context.string_tables.name_to_table.clear();
271 }
272
273 self.prologue()?;
274
275 self.skip_deltas = true;
276 let observers = mem::take(&mut self.observers);
277
278 let mut first_fp_checked = false;
279 let mut last_fp_checked = false;
280
281 while let Some(mut message) = self.reader.read_next_message()? {
282 self.context.previous_tick = self.context.tick;
283 self.context.tick = message.tick;
284
285 if message.msg_type == EDemoCommands::DemFullPacket {
286 self.context.last_full_packet_tick = self.context.tick;
287 }
288
289 let next_fp = self.context.last_full_packet_tick == u32::MAX
290 || (target_tick - self.context.last_full_packet_tick) > 1800;
291
292 if message.msg_type == EDemoCommands::DemFullPacket {
293 if next_fp && first_fp_checked {
294 message.msg_type = EDemoCommands::DemStringTables;
295 message.buf = CDemoFullPacket::decode(message.buf.as_slice())?
296 .string_table
297 .unwrap()
298 .encode_to_vec();
299 }
300
301 self.on_demo_command(message.msg_type, message.buf.as_slice())?;
302 }
303
304 if last_fp_checked {
305 self.on_demo_command(message.msg_type, message.buf.as_slice())?;
306 }
307
308 if message.msg_type == EDemoCommands::DemFullPacket && !first_fp_checked {
309 first_fp_checked = true;
310 }
311
312 if message.msg_type == EDemoCommands::DemFullPacket && !next_fp {
313 last_fp_checked = true;
314 self.skip_deltas = false;
315 }
316
317 if self.context.tick >= target_tick && first_fp_checked {
318 break;
319 }
320 }
321
322 self.skip_deltas = false;
323 self.observers = observers;
324
325 Ok(())
326 }
327
328 pub fn run_to_tick(&mut self, mut target_tick: u32) -> Result<(), ParserError> {
330 assert!(target_tick > self.context.tick || self.context.tick == u32::MAX);
331
332 self.prologue()?;
333
334 target_tick = min(target_tick, self.last_tick);
335
336 while let Some(message) = self.reader.read_next_message()? {
337 self.on_tick_start(message.tick)?;
338 self.on_demo_command(message.msg_type, message.buf.as_slice())?;
339 self.on_tick_end(message.tick)?;
340 if self.context.tick >= target_tick {
341 break;
342 }
343 }
344
345 Ok(())
346 }
347
348 fn on_demo_command(&mut self, msg_type: EDemoCommands, msg: &[u8]) -> Result<(), ParserError> {
349 match msg_type {
350 EDemoCommands::DemSendTables => self.dem_send_tables(msg)?,
351 EDemoCommands::DemClassInfo => self.dem_class_info(msg)?,
352 EDemoCommands::DemPacket | EDemoCommands::DemSignonPacket => self.dem_packet(msg)?,
353 EDemoCommands::DemFullPacket => self.dem_full_packet(msg)?,
354 EDemoCommands::DemStringTables => self.dem_string_tables(msg)?,
355 _ => {}
356 };
357
358 try_observers!(self, on_demo_command(&self.context, msg_type, msg))?;
359 Ok(())
360 }
361
362 fn on_net_message(&mut self, msg_type: NetMessages, msg: &[u8]) -> Result<(), ParserError> {
363 if msg_type == NetMessages::NetTick {
364 self.context.net_tick = CNetMsgTick::decode(msg)?.tick();
365 }
366
367 try_observers!(self, on_net_message(&self.context, msg_type, msg))?;
368 Ok(())
369 }
370
371 fn on_svc_message(&mut self, msg_type: SvcMessages, msg: &[u8]) -> Result<(), ParserError> {
372 match msg_type {
373 SvcMessages::SvcServerInfo => self.server_info(msg)?,
374 SvcMessages::SvcCreateStringTable => self.create_string_table(msg)?,
375 SvcMessages::SvcUpdateStringTable => self.update_string_table(msg)?,
376 SvcMessages::SvcPacketEntities => self.packet_entities(msg)?,
377 _ => {}
378 }
379
380 try_observers!(self, on_svc_message(&self.context, msg_type, msg))?;
381 Ok(())
382 }
383
384 fn on_base_user_message(
385 &mut self,
386 msg_type: EBaseUserMessages,
387 msg: &[u8],
388 ) -> Result<(), ParserError> {
389 try_observers!(self, on_base_user_message(&self.context, msg_type, msg))?;
390 Ok(())
391 }
392
393 fn on_base_game_event(
394 &mut self,
395 msg_type: EBaseGameEvents,
396 msg: &[u8],
397 ) -> Result<(), ParserError> {
398 try_observers!(self, on_base_game_event(&self.context, msg_type, msg))?;
399 Ok(())
400 }
401
402 fn on_dota_user_message(
403 &mut self,
404 msg_type: EDotaUserMessages,
405 msg: &[u8],
406 ) -> Result<(), ParserError> {
407 if msg_type == EDotaUserMessages::DotaUmCombatLogDataHltv {
408 let entry = CMsgDotaCombatLogEntry::decode(msg)?;
409 self.combat_log.push_back(entry);
410 }
411
412 try_observers!(self, on_dota_user_message(&self.context, msg_type, msg))?;
413 Ok(())
414 }
415
416 pub(crate) fn on_tick_start(&mut self, msg_tick: u32) -> Result<(), ParserError> {
417 self.context.previous_tick = self.context.tick;
418 self.context.tick = msg_tick;
419
420 if self.context.previous_tick == msg_tick {
421 return Ok(());
422 }
423
424 try_observers!(self, on_tick_start(&self.context))?;
425 Ok(())
426 }
427
428 pub(crate) fn on_tick_end(&mut self, msg_tick: u32) -> Result<(), ParserError> {
429 if self.context.previous_tick == msg_tick {
430 return Ok(());
431 }
432
433 if let Ok(names) = self.context.string_tables.get_by_name("CombatLogNames") {
434 while let Some(log) = self.combat_log.pop_front() {
435 self.on_combat_log(&CombatLogEntry { names, log })?;
436 }
437 }
438
439 try_observers!(self, on_tick_end(&self.context))?;
440 Ok(())
441 }
442
443 pub(crate) fn on_combat_log(&self, entry: &CombatLogEntry) -> Result<(), ParserError> {
444 try_observers!(self, on_combat_log(&self.context, entry))?;
445 Ok(())
446 }
447
448 fn dem_send_tables(&mut self, msg: &[u8]) -> Result<(), ParserError> {
449 let send_tables = CDemoSendTables::decode(msg)?;
450 let mut reader = Reader::new(send_tables.data());
451 let amount = reader.read_var_u32();
452 let buf = reader.read_bytes(amount);
453
454 let fs = CSvcMsgFlattenedSerializer::decode(buf.as_slice())?;
455
456 let resolve = |p: Option<i32>| -> Box<str> {
457 if let Some(i) = p {
458 return fs.symbols[i as usize].clone().into();
459 }
460 "".into()
461 };
462
463 let mut fields: Vec<Rc<Field>> = vec![];
464 let mut field_types: HashMap<Box<str>, Rc<FieldType>> = HashMap::default();
465
466 for s in fs.serializers.iter() {
467 let serializer_name = fs.symbols[s.serializer_name_sym() as usize].clone();
468 let mut serializer = Serializer::default();
469
470 for i in s.fields_index.iter().map(|x| *x as usize) {
471 let current_field = &fs.fields[i];
472 let field_serializer_name = resolve(current_field.field_serializer_name_sym);
473
474 if i >= fields.len() {
475 let var_type_str = resolve(current_field.var_type_sym);
476 let var_name = resolve(current_field.var_name_sym);
477
478 let current_field_serializer = self
479 .context
480 .serializers
481 .get(&field_serializer_name)
482 .cloned();
483
484 let field_type = field_types
485 .entry(var_type_str.clone())
486 .or_insert_with(|| FieldType::new(var_type_str.clone().as_ref()).into())
487 .clone();
488
489 let properties = FieldProperties {
490 encoder: match var_name.as_ref() {
491 "m_flSimulationTime" | "m_flAnimTime" => Some(Encoder::SimTime),
492 "m_flRuneTime" => Some(Encoder::RuneTime),
493 _ => Encoder::from_str(&resolve(current_field.var_encoder_sym)),
494 },
495 encoder_flags: current_field.encode_flags(),
496 bit_count: current_field.bit_count(),
497 low_value: current_field.low_value(),
498 high_value: current_field.high_value(),
499 };
500
501 let model = if let Some(serializer) = current_field_serializer {
502 if field_type.pointer {
503 FieldModel::FixedTable(serializer)
504 } else {
505 FieldModel::VariableTable(serializer)
506 }
507 } else if field_type.count.is_some_and(|x| x > 0)
508 && field_type.base.as_ref() != "char"
509 {
510 FieldModel::FixedArray
511 } else if field_type.base.as_ref() == "CUtlVector"
512 || field_type.base.as_ref() == "CNetworkUtlVectorBase"
513 {
514 FieldModel::VariableArray(Decoder::from_field(
515 field_type.generic.as_ref().unwrap(),
516 properties,
517 ))
518 } else {
519 FieldModel::Simple
520 };
521
522 let decoder = match model {
523 FieldModel::Simple | FieldModel::FixedArray => {
524 Decoder::from_field(&field_type, properties)
525 }
526 FieldModel::VariableArray(_) => Decoder::Unsigned32,
527 FieldModel::FixedTable(_) => Decoder::Boolean,
528 FieldModel::VariableTable(_) => Decoder::Unsigned32,
529 };
530
531 let field = Field {
532 var_name,
533 field_type,
534 model,
535
536 decoder,
537 };
538 fields.push(field.into());
539 }
540 serializer.fields.push(fields[i].clone());
541 }
542 self.context
543 .serializers
544 .insert(serializer_name.into(), serializer.into());
545 }
546 Ok(())
547 }
548
549 fn dem_class_info(&mut self, msg: &[u8]) -> Result<(), ParserError> {
550 let info = CDemoClassInfo::decode(msg)?;
551 for class in info.classes {
552 let class_id = class.class_id();
553 let network_name = class.network_name();
554
555 let serializer = self.context.serializers[network_name].clone();
556
557 let class = Rc::new(Class::new(class_id, network_name.into(), serializer));
558
559 self.context.classes.classes_vec.push(class.clone());
560 self.context
561 .classes
562 .classes_by_name
563 .insert(network_name.into(), class);
564 }
565 Ok(())
566 }
567
568 fn dem_packet(&mut self, msg: &[u8]) -> Result<(), ParserError> {
569 let packet = CDemoPacket::decode(msg)?;
570 let mut packet_reader = Reader::new(packet.data());
571 while packet_reader.bytes_remaining() != 0 {
572 let msg_type = packet_reader.read_ubit_var() as i32;
573 let size = packet_reader.read_var_u32();
574 let packet_buf = packet_reader.read_bytes(size);
575
576 if let Ok(msg) = EDotaUserMessages::try_from(msg_type) {
577 self.on_dota_user_message(msg, &packet_buf)?;
578 } else if let Ok(msg) = SvcMessages::try_from(msg_type) {
579 self.on_svc_message(msg, &packet_buf)?;
580 } else if let Ok(msg) = EBaseUserMessages::try_from(msg_type) {
581 self.on_base_user_message(msg, &packet_buf)?;
582 } else if let Ok(msg) = EBaseGameEvents::try_from(msg_type) {
583 self.on_base_game_event(msg, &packet_buf)?;
584 } else if let Ok(msg) = NetMessages::try_from(msg_type) {
585 self.on_net_message(msg, &packet_buf)?;
586 }
587 }
588
589 Ok(())
590 }
591
592 fn dem_full_packet(&mut self, msg: &[u8]) -> Result<(), ParserError> {
593 let packet = CDemoFullPacket::decode(msg)?;
594
595 if self.context.last_full_packet_tick == u32::MAX || self.skip_deltas {
596 self.on_demo_command(
597 EDemoCommands::DemStringTables,
598 &packet.string_table.unwrap().encode_to_vec(),
599 )?;
600
601 self.on_demo_command(
602 EDemoCommands::DemPacket,
603 &packet.packet.unwrap().encode_to_vec(),
604 )?;
605 }
606
607 self.context.last_full_packet_tick = self.context.tick;
608
609 Ok(())
610 }
611
612 fn dem_string_tables(&mut self, msg: &[u8]) -> Result<(), ParserError> {
613 let cmd = CDemoStringTables::decode(msg)?;
614 for table in cmd.tables.iter() {
615 let x = self
616 .context
617 .string_tables
618 .get_by_name_mut(table.table_name())?;
619 if table.items.len() < x.items.len() {
620 return Ok(());
621 }
622 x.items
623 .resize_with(table.items.len(), StringTableRow::default);
624 for (i, item) in table.items.iter().enumerate() {
625 x.items[i].index = i as i32;
626 x.items[i].key = item.str().to_string();
627 x.items[i].value = Rc::new(item.data().to_vec()).into();
628 if table.table_name() == "instancebaseline" {
629 self.context.baselines.add_baseline(
630 item.str().parse().unwrap(),
631 x.items[i].value.as_ref().unwrap().clone(),
632 );
633 }
634 }
635 }
636
637 Ok(())
638 }
639
640 fn packet_entities(&mut self, msg: &[u8]) -> Result<(), ParserError> {
641 let packet = CSvcMsgPacketEntities::decode(msg)?;
642 let mut reader = Reader::new(packet.entity_data());
643
644 let mut index = usize::MAX;
645
646 for _ in 0..packet.updated_entries() {
647 index = index.wrapping_add((reader.read_ubit_var() + 1) as usize);
648
649 let cmd = reader.read_bits(2);
650 if cmd == 1 {
651 continue;
652 }
653
654 match EntityEvents::from_cmd(cmd) {
655 EntityEvents::Created => {
656 let class_id = reader.read_bits(self.context.classes.class_id_size) as i32;
657 let serial = reader.read_bits(17);
658 let _ = reader.read_var_u32();
659
660 let class = self.context.classes.get_by_id_rc(class_id as usize).clone();
661
662 let entity_baseline = self
663 .context
664 .baselines
665 .states
666 .entry(class_id)
667 .or_insert_with(|| {
668 let mut state = FieldState::default();
669 self.field_reader.read_fields(
670 &mut Reader::new(&self.context.baselines.baselines[&class_id]),
671 &class.serializer,
672 &mut state,
673 );
674 state
675 })
676 .clone();
677
678 self.context.entities.entities_vec[index] = Some(Entity::new(
679 index as u32,
680 serial,
681 class.clone(),
682 entity_baseline,
683 ));
684
685 let entity = self.context.entities.entities_vec[index].as_mut().unwrap();
686
687 self.field_reader.read_fields(
688 &mut reader,
689 &entity.class.serializer,
690 &mut entity.state,
691 );
692
693 try_observers!(
694 self,
695 on_entity(
696 &self.context,
697 EntityEvents::Created,
698 self.context.entities.entities_vec[index].as_ref().unwrap()
699 )
700 )?;
701 }
702 EntityEvents::Updated => {
703 let entity = self.context.entities.entities_vec[index].as_mut().unwrap();
704
705 self.field_reader.read_fields(
706 &mut reader,
707 &entity.class.serializer,
708 &mut entity.state,
709 );
710
711 try_observers!(
712 self,
713 on_entity(
714 &self.context,
715 EntityEvents::Updated,
716 self.context.entities.entities_vec[index].as_ref().unwrap()
717 )
718 )?;
719 }
720 EntityEvents::Deleted => {
721 if let Some(entity) = self.context.entities.entities_vec[index].as_ref() {
722 try_observers!(
723 self,
724 on_entity(&self.context, EntityEvents::Deleted, entity)
725 )?;
726 }
727 self.context.entities.entities_vec[index] = None;
728 }
729 }
730 }
731
732 Ok(())
733 }
734
735 fn update_string_table(&mut self, msg: &[u8]) -> Result<(), ParserError> {
736 let table_msg = CSvcMsgUpdateStringTable::decode(msg)?;
737 let table = self
738 .context
739 .string_tables
740 .tables
741 .get_mut(table_msg.table_id() as usize)
742 .unwrap();
743
744 table.parse(
745 &mut self.context.baselines,
746 table_msg.string_data(),
747 table_msg.num_changed_entries(),
748 )?;
749
750 Ok(())
751 }
752
753 fn create_string_table(&mut self, msg: &[u8]) -> Result<(), ParserError> {
754 let table_msg = CSvcMsgCreateStringTable::decode(msg)?;
755
756 let mut table = StringTable {
757 index: self.context.string_tables.tables.len() as i32,
758 name: table_msg.name().into(),
759 items: vec![],
760 user_data_fixed_size: table_msg.user_data_fixed_size(),
761 user_data_size: table_msg.user_data_size(),
762 flags: table_msg.flags() as u32,
763 var_int_bit_counts: table_msg.using_varint_bitcounts(),
764 keys: RefCell::new(vec![String::default(); 32]),
765 };
766
767 let buf = if table_msg.data_compressed() {
768 let mut decoder = snap::raw::Decoder::new();
769 decoder.decompress_vec(table_msg.string_data())?
770 } else {
771 table_msg.string_data().into()
772 };
773
774 table.parse(
775 &mut self.context.baselines,
776 buf.as_slice(),
777 table_msg.num_entries(),
778 )?;
779
780 self.context
781 .string_tables
782 .name_to_table
783 .insert(table.name().into(), table.index as usize);
784 self.context.string_tables.tables.push(table);
785
786 Ok(())
787 }
788
789 fn server_info(&mut self, msg: &[u8]) -> Result<(), ParserError> {
790 let info = CSvcMsgServerInfo::decode(msg)?;
791 self.context.classes.class_id_size = (f64::log2(info.max_classes() as f64) + 1.0) as u32;
792
793 let game_dir = info.game_dir();
794 if let Some(start) = game_dir.find("dota_v") {
795 let start = start + "dota_v".len();
796 if let Some(end) = game_dir[start..].find('/') {
797 let build_str = &game_dir[start..start + end];
798 let build = build_str.parse::<u32>().unwrap();
799 self.context.game_build = build;
800 }
801 }
802 Ok(())
803 }
804}
805
806#[allow(unused_variables)]
810pub trait Observer {
811 fn on_demo_command(
812 &mut self,
813 ctx: &Context,
814 msg_type: EDemoCommands,
815 msg: &[u8],
816 ) -> ObserverResult {
817 Ok(())
818 }
819
820 fn on_net_message(
821 &mut self,
822 ctx: &Context,
823 msg_type: NetMessages,
824 msg: &[u8],
825 ) -> ObserverResult {
826 Ok(())
827 }
828
829 fn on_svc_message(
830 &mut self,
831 ctx: &Context,
832 msg_type: SvcMessages,
833 msg: &[u8],
834 ) -> ObserverResult {
835 Ok(())
836 }
837
838 fn on_base_user_message(
839 &mut self,
840 ctx: &Context,
841 msg_type: EBaseUserMessages,
842 msg: &[u8],
843 ) -> ObserverResult {
844 Ok(())
845 }
846
847 fn on_base_game_event(
848 &mut self,
849 ctx: &Context,
850 msg_type: EBaseGameEvents,
851 msg: &[u8],
852 ) -> ObserverResult {
853 Ok(())
854 }
855
856 fn on_dota_user_message(
857 &mut self,
858 ctx: &Context,
859 msg_type: EDotaUserMessages,
860 msg: &[u8],
861 ) -> ObserverResult {
862 Ok(())
863 }
864
865 fn on_tick_start(&mut self, ctx: &Context) -> ObserverResult {
866 Ok(())
867 }
868
869 fn on_tick_end(&mut self, ctx: &Context) -> ObserverResult {
870 Ok(())
871 }
872
873 fn on_entity(&mut self, ctx: &Context, event: EntityEvents, entity: &Entity) -> ObserverResult {
874 Ok(())
875 }
876
877 fn on_combat_log(&mut self, ctx: &Context, cle: &CombatLogEntry) -> ObserverResult {
878 Ok(())
879 }
880
881 fn epilogue(&mut self, ctx: &Context) -> ObserverResult {
882 Ok(())
883 }
884}