Skip to main content

source2_demo/parser/demo/
runner.rs

1//! Demo runner implementations for parser execution.
2//!
3//! This module provides the [`DemoRunner`] trait which defines methods for
4//! controlling replay parsing execution.
5
6use crate::error::ParserError;
7use crate::parser::demo::DemoMessages;
8use crate::parser::Parser;
9use crate::proto::*;
10use crate::reader::*;
11use crate::Entity;
12use std::cmp::min;
13use std::mem;
14
15/// Trait for controlling replay parsing execution.
16///
17/// Provides methods to run the parser to completion, to a specific tick,
18/// or to jump to a tick without full processing.
19pub trait DemoRunner {
20    /// Processes the entire replay from start to finish.
21    ///
22    /// This method processes all demo commands sequentially, calling registered
23    /// observers for each event. The final packet is [`CDemoFileInfo`].
24    ///
25    /// # Errors
26    ///
27    /// Returns [`ParserError`] if parsing fails.
28    ///
29    /// # Examples
30    ///
31    /// ```no_run
32    /// use source2_demo::prelude::*;
33    ///
34    /// # fn main() -> anyhow::Result<()> {
35    /// let replay = std::fs::read("replay.dem")?;
36    /// let mut parser = Parser::new(&replay)?;
37    /// parser.register_observer::<MyObserver>();
38    /// parser.run_to_end()?;
39    /// # Ok(())
40    /// # }
41    /// # #[derive(Default)]
42    /// # struct MyObserver;
43    /// # impl Observer for MyObserver {}
44    /// ```
45    fn run_to_end(&mut self) -> Result<(), ParserError>;
46
47    /// Processes the replay up to a specific tick.
48    ///
49    /// Stops parsing when the specified tick is reached. All observers are
50    /// called for events up to and including the target tick.
51    ///
52    /// # Arguments
53    ///
54    /// * `target_tick` - The tick to parse up to (inclusive)
55    ///
56    /// # Errors
57    ///
58    /// Returns [`ParserError`] if parsing fails.
59    ///
60    /// # Examples
61    ///
62    /// ```no_run
63    /// use source2_demo::prelude::*;
64    ///
65    /// # fn main() -> anyhow::Result<()> {
66    /// let replay = std::fs::File::open("replay.dem")?;
67    /// let mut parser = Parser::from_reader(&replay)?;
68    /// 
69    /// // Process first 5 minutes (30 ticks per second * 60 seconds * 5 minutes)
70    /// parser.run_to_tick(9000)?;
71    /// # Ok(())
72    /// # }
73    /// ```
74    fn run_to_tick(&mut self, target_tick: u32) -> Result<(), ParserError>;
75
76    /// Jumps to a specific tick without full processing.
77    ///
78    /// This is an optimized method that seeks to the target tick without
79    /// calling observers for intermediate events. Useful for jumping to
80    /// a specific point in a replay quickly.
81    ///
82    /// After jumping, you can continue parsing normally with observers active.
83    ///
84    /// # Arguments
85    ///
86    /// * `target_tick` - The tick to jump to
87    ///
88    /// # Errors
89    ///
90    /// Returns [`ParserError`] if seeking fails.
91    ///
92    /// # Examples
93    ///
94    /// ```no_run
95    /// use source2_demo::prelude::*;
96    ///
97    /// # fn main() -> anyhow::Result<()> {
98    /// let replay = std::fs::File::open("replay.dem")?;
99    /// let mut parser = Parser::from_reader(&replay)?;
100    /// 
101    /// // Jump to 10 minutes in
102    /// parser.jump_to_tick(18000)?;
103    /// 
104    /// // Now register observers and continue
105    /// parser.register_observer::<MyObserver>();
106    /// parser.run_to_tick(20000)?;
107    /// # Ok(())
108    /// # }
109    /// # #[derive(Default)]
110    /// # struct MyObserver;
111    /// # impl Observer for MyObserver {}
112    /// ```
113    fn jump_to_tick(&mut self, target_tick: u32) -> Result<(), ParserError>;
114}
115
116impl<'a, R> DemoRunner for Parser<'a, R>
117where
118    R: BitsReader + MessageReader,
119{
120    fn run_to_end(&mut self) -> Result<(), ParserError> {
121        self.prologue()?;
122
123        while let Some(message) = self.reader.read_next_message()? {
124            self.on_tick_start(message.tick)?;
125            self.on_demo_command(message.msg_type, message.buf.as_slice())?;
126        }
127        self.on_tick_end()?;
128
129        Ok(())
130    }
131
132    fn run_to_tick(&mut self, target_tick: u32) -> Result<(), ParserError> {
133        assert!(target_tick > self.context.tick || self.context.tick == u32::MAX);
134
135        self.prologue()?;
136
137        let target_tick = min(target_tick, self.last_tick);
138
139        while let Some(message) = self.reader.read_next_message()? {
140            self.on_tick_start(message.tick)?;
141            self.on_demo_command(message.msg_type, message.buf.as_slice())?;
142            if self.context.tick >= target_tick {
143                self.on_tick_end()?;
144                break;
145            }
146        }
147
148        Ok(())
149    }
150
151    fn jump_to_tick(&mut self, target_tick: u32) -> Result<(), ParserError> {
152        let fp_delta = if cfg!(feature = "deadlock") {
153            3600
154        } else {
155            1800
156        };
157
158        let target_tick = min(target_tick, self.last_tick);
159
160        if target_tick < self.context.tick {
161            self.context.last_full_packet_tick = u32::MAX;
162            self.context.tick = u32::MAX;
163            self.context.net_tick = u32::MAX;
164            self.reader.seek(16);
165
166            self.context.entities.entities_vec = vec![Entity::default(); 8192];
167
168            self.context.string_tables.tables.clear();
169            self.context.string_tables.name_to_table.clear();
170            self.context.game_events.list.clear();
171        }
172
173        self.prologue()?;
174
175        self.skip_deltas = true;
176        let observers = mem::take(&mut self.observers);
177
178        let mut first_fp_checked = self.context.last_full_packet_tick != u32::MAX;
179        let mut last_fp_checked = false;
180
181        while let Some(mut message) = self.reader.read_next_message()? {
182            self.context.previous_tick = self.context.tick;
183            self.context.tick = message.tick;
184
185            if message.msg_type == EDemoCommands::DemFullPacket {
186                self.context.last_full_packet_tick = self.context.tick;
187            }
188
189            let next_fp = self.context.last_full_packet_tick == u32::MAX
190                || self.context.last_full_packet_tick < target_tick
191                    && (target_tick - self.context.last_full_packet_tick) > fp_delta;
192
193            if message.msg_type == EDemoCommands::DemFullPacket {
194                if next_fp && first_fp_checked {
195                    message.msg_type = EDemoCommands::DemStringTables;
196                    message.buf = CDemoFullPacket::decode(message.buf.as_slice())?
197                        .string_table
198                        .unwrap()
199                        .encode_to_vec();
200                }
201
202                self.on_demo_command(message.msg_type, message.buf.as_slice())?;
203            }
204
205            if last_fp_checked {
206                self.on_demo_command(message.msg_type, message.buf.as_slice())?;
207            }
208
209            if message.msg_type == EDemoCommands::DemFullPacket && !first_fp_checked {
210                first_fp_checked = true;
211            }
212
213            if message.msg_type == EDemoCommands::DemFullPacket && !next_fp {
214                last_fp_checked = true;
215                self.skip_deltas = false;
216            }
217
218            if self.context.tick >= target_tick && first_fp_checked {
219                break;
220            }
221        }
222
223        self.skip_deltas = false;
224        self.observers = observers;
225
226        Ok(())
227    }
228}