source2_demo/parser/context.rs
1//! Parser context containing the current replay state.
2//!
3//! The [`Context`] struct maintains all the state information about the replay
4//! as it's being parsed, including entities, string tables, game events, and
5//! the current tick.
6
7use crate::entity::field::*;
8use crate::entity::*;
9use crate::event::*;
10use crate::string_table::*;
11use crate::HashMap;
12use source2_demo_protobufs::CDemoFileInfo;
13use std::rc::Rc;
14
15/// Current replay state accessible to observers.
16///
17/// The context is passed to all observer callbacks and provides access to:
18/// - Entity state and properties
19/// - String tables
20/// - Game event definitions
21/// - Current tick and timing information
22/// - Replay metadata
23///
24/// # Examples
25///
26/// ## Accessing entities
27///
28/// ```no_run
29/// use source2_demo::prelude::*;
30///
31/// #[derive(Default)]
32/// struct HeroStats;
33///
34/// #[observer]
35/// #[uses_entities]
36/// impl HeroStats {
37/// #[on_tick_start]
38/// fn on_tick_start(&mut self, ctx: &Context) -> ObserverResult {
39/// for entity in ctx.entities().iter() {
40/// if entity.class().name().starts_with("CDOTA_Unit_Hero_") {
41/// let health: i32 = property!(entity, "m_iHealth");
42/// println!("{}: {}", entity.class().name(), health);
43/// }
44/// }
45/// Ok(())
46/// }
47/// }
48/// ```
49///
50/// ## Accessing string tables
51///
52/// ```no_run
53/// use source2_demo::prelude::*;
54///
55/// #[derive(Default)]
56/// struct TableReader;
57///
58/// #[observer]
59/// #[uses_string_tables]
60/// impl TableReader {
61/// #[on_tick_start]
62/// fn on_tick_start(&mut self, ctx: &Context) -> ObserverResult {
63/// if let Ok(table) = ctx.string_tables().get_by_name("ActiveModifiers") {
64/// println!("Active modifiers: {}", table.iter().count());
65/// }
66/// Ok(())
67/// }
68/// }
69/// ```
70pub struct Context {
71 pub(crate) classes: Classes,
72 pub(crate) entities: Entities,
73 pub(crate) string_tables: StringTables,
74 pub(crate) game_events: GameEventList,
75
76 pub(crate) tick: u32,
77 pub(crate) previous_tick: u32,
78 pub(crate) net_tick: u32,
79
80 pub(crate) game_build: u32,
81 pub(crate) replay_info: CDemoFileInfo,
82
83 pub(crate) baselines: BaselineContainer,
84 pub(crate) serializers: HashMap<Box<str>, Rc<Serializer>>,
85 pub(crate) last_full_packet_tick: u32,
86}
87
88impl Default for Context {
89 fn default() -> Self {
90 Context {
91 classes: Classes::default(),
92 entities: Entities::default(),
93 string_tables: StringTables::default(),
94 game_events: Default::default(),
95 tick: u32::MAX,
96 previous_tick: u32::MAX,
97 net_tick: u32::MAX,
98 game_build: 0,
99 replay_info: CDemoFileInfo::default(),
100 baselines: BaselineContainer::default(),
101 serializers: HashMap::default(),
102 last_full_packet_tick: u32::MAX,
103 }
104 }
105}
106
107impl Context {
108 pub(crate) fn new(replay_info: CDemoFileInfo) -> Self {
109 Context {
110 classes: Classes::default(),
111 entities: Entities::default(),
112 string_tables: StringTables::default(),
113 game_events: GameEventList::default(),
114 tick: u32::MAX,
115 previous_tick: u32::MAX,
116 net_tick: u32::MAX,
117 game_build: 0,
118 replay_info,
119 baselines: BaselineContainer::default(),
120 serializers: HashMap::default(),
121 last_full_packet_tick: u32::MAX,
122 }
123 }
124}
125
126impl Context {
127 /// Returns a reference to the entity container.
128 ///
129 /// Use this to access all entities in the game and query their properties.
130 ///
131 /// # Examples
132 ///
133 /// ```no_run
134 /// use source2_demo::prelude::*;
135 ///
136 /// # fn example(ctx: &Context) -> anyhow::Result<()> {
137 /// // Get entity by index
138 /// let entity = ctx.entities().get_by_index(0)?;
139 ///
140 /// // Find entity by class name
141 /// let player_resource = ctx.entities().get_by_class_name("CDOTA_PlayerResource")?;
142 ///
143 /// // Iterate all entities
144 /// for entity in ctx.entities().iter() {
145 /// println!("{}", entity.class().name());
146 /// }
147 /// # Ok(())
148 /// # }
149 /// ```
150 pub fn classes(&self) -> &Classes {
151 &self.classes
152 }
153
154 /// Returns a reference to the entity container.
155 ///
156 /// Provides access to all game entities and their properties.
157 /// Requires `Interests::ENTITY_STATE` to be populated.
158 pub fn entities(&self) -> &Entities {
159 &self.entities
160 }
161
162 /// Returns a reference to the string tables.
163 ///
164 /// String tables contain game data like hero names, item names, etc.
165 /// Requires `Interests::STRING_TABLE_STATE` to be populated.
166 pub fn string_tables(&self) -> &StringTables {
167 &self.string_tables
168 }
169
170 /// Returns a reference to the game event list.
171 ///
172 /// Contains definitions for all game events in the replay.
173 pub fn game_events(&self) -> &GameEventList {
174 &self.game_events
175 }
176
177 /// Returns the current tick number.
178 ///
179 /// The tick represents the current game simulation step.
180 /// Typically runs at 30 ticks per second.
181 pub fn tick(&self) -> u32 {
182 self.tick
183 }
184
185 /// Returns the current network tick.
186 ///
187 /// The network tick from the last processed packet.
188 pub fn net_tick(&self) -> u32 {
189 self.net_tick
190 }
191
192 /// Returns the game build number.
193 ///
194 /// Identifies the specific version of the game that created this replay.
195 pub fn game_build(&self) -> u32 {
196 self.game_build
197 }
198
199 /// Returns replay file metadata.
200 ///
201 /// Contains information about the replay including duration, map, and game
202 /// info.
203 pub fn replay_info(&self) -> &CDemoFileInfo {
204 &self.replay_info
205 }
206}