source2_demo/parser/
mod.rs

1mod 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    /// Creates a new instance of parser and performs validation of a replay file.
41    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    /// Registers a new observer and returns `Rc<RefCell<T>>` of it.
83    /// Observer struct must implement Observer and Default traits.
84    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}