source2_demo/parser/
mod.rs1mod context;
2mod demo;
3mod observer;
4
5pub use context::*;
6pub use demo::runner::*;
7pub use observer::*;
8
9use crate::error::*;
10use crate::proto::*;
11use crate::reader::*;
12use std::cell::RefCell;
13use std::rc::Rc;
14
15use crate::parser::demo::DemoCommands;
16use crate::try_observers;
17#[cfg(feature = "dota")]
18use std::collections::VecDeque;
19
20pub struct Parser<'a> {
21 pub(crate) reader: Reader<'a>,
22 pub(crate) field_reader: FieldReader,
23
24 pub(crate) observers: Vec<Rc<RefCell<dyn Observer + 'a>>>,
25 pub(crate) observer_masks: Vec<Interests>,
26 pub(crate) global_mask: Interests,
27
28 #[cfg(feature = "dota")]
29 pub(crate) combat_log: VecDeque<CMsgDotaCombatLogEntry>,
30
31 pub(crate) prologue_completed: bool,
32 pub(crate) skip_deltas: bool,
33
34 pub(crate) replay_info: CDemoFileInfo,
35 pub(crate) last_tick: u32,
36 pub(crate) context: Context,
37}
38
39impl<'a> Parser<'a> {
40 pub fn new(replay: &'a [u8]) -> Result<Self, ParserError> {
42 let mut reader = Reader::new(replay);
43
44 if replay.len() < 16 || reader.read_bytes(8) != b"PBDEMS2\0" {
45 return Err(ParserError::WrongMagic);
46 };
47
48 reader.read_bytes(8);
49
50 let replay_info = reader.read_replay_info()?;
51 let last_tick = replay_info.playback_ticks() as u32;
52
53 Ok(Parser {
54 reader,
55 field_reader: FieldReader::default(),
56
57 observers: Vec::default(),
58 observer_masks: Vec::default(),
59 global_mask: Interests::empty(),
60
61 #[cfg(feature = "dota")]
62 combat_log: VecDeque::default(),
63
64 prologue_completed: false,
65 skip_deltas: false,
66
67 context: Context::new(replay_info.clone()),
68
69 replay_info,
70 last_tick,
71 })
72 }
73
74 pub fn context(&self) -> &Context {
75 &self.context
76 }
77
78 pub fn replay_info(&self) -> &CDemoFileInfo {
79 &self.replay_info
80 }
81
82 pub fn register_observer<T>(&mut self) -> Rc<RefCell<T>>
85 where
86 T: Observer + Default + 'a,
87 {
88 let rc = Rc::new(RefCell::new(T::default()));
89 let mask = rc.borrow().interests();
90 self.global_mask |= mask;
91 self.observer_masks.push(mask);
92 self.observers.push(rc.clone());
93 rc.clone()
94 }
95
96 #[inline]
97 fn anyone_interested(&self, flag: Interests) -> bool {
98 self.global_mask.intersects(flag)
99 }
100
101 pub(crate) fn prologue(&mut self) -> Result<(), ParserError> {
102 if self.prologue_completed && self.context.tick != u32::MAX {
103 return Ok(());
104 }
105
106 while let Some(message) = self.reader.read_next_message()? {
107 if self.prologue_completed
108 && (message.msg_type == EDemoCommands::DemSendTables
109 || message.msg_type == EDemoCommands::DemClassInfo)
110 {
111 continue;
112 }
113
114 self.on_demo_command(message.msg_type, message.buf.as_slice())?;
115
116 if message.msg_type == EDemoCommands::DemSyncTick {
117 self.prologue_completed = true;
118 break;
119 }
120 }
121
122 Ok(())
123 }
124
125 pub(crate) fn on_demo_command(
126 &mut self,
127 msg_type: EDemoCommands,
128 msg: &[u8],
129 ) -> Result<(), ParserError> {
130 match msg_type {
131 EDemoCommands::DemSendTables => {
132 self.dem_send_tables(CDemoSendTables::decode(msg)?)?;
133 }
134 EDemoCommands::DemClassInfo => {
135 self.dem_class_info(CDemoClassInfo::decode(msg)?)?;
136 }
137 EDemoCommands::DemPacket | EDemoCommands::DemSignonPacket => {
138 self.dem_packet(CDemoPacket::decode(msg)?)?;
139 }
140 EDemoCommands::DemFullPacket => self.dem_full_packet(CDemoFullPacket::decode(msg)?)?,
141 EDemoCommands::DemStringTables => {
142 self.dem_string_tables(CDemoStringTables::decode(msg)?)?
143 }
144 EDemoCommands::DemStop => {
145 self.dem_stop()?;
146 }
147 _ => {}
148 };
149
150 try_observers!(self, DEMO, on_demo_command(&self.context, msg_type, msg))?;
151 Ok(())
152 }
153
154 #[cfg(feature = "deadlock")]
155 pub fn deadlock_match_details(&mut self) -> Result<CMsgMatchMetaDataContents, ParserError> {
156 self.reader.read_deadlock_match_details()
157 }
158}