source2_demo/entity/class.rs
1use crate::entity::field::Serializer;
2use crate::error::ClassError;
3use crate::HashMap;
4use std::rc::Rc;
5
6/// Container for all entity classes in a replay.
7///
8/// Classes define the types of entities that exist in the game. Each entity
9/// belongs to a class which specifies:
10/// - What properties it has
11/// - How those properties are encoded
12/// - The class name (e.g., "CDOTA_Unit_Hero_Axe")
13///
14/// # Examples
15///
16/// ## Iterating all classes
17///
18/// ```no_run
19/// use source2_demo::prelude::*;
20///
21/// # fn example(ctx: &Context) {
22/// for class in ctx.classes().iter() {
23/// println!("Class ID {}: {}", class.id(), class.name());
24/// }
25/// # }
26/// ```
27///
28/// ## Finding a specific class
29///
30/// ```no_run
31/// use source2_demo::prelude::*;
32///
33/// # fn example(ctx: &Context) -> anyhow::Result<()> {
34/// // Get by class name
35/// let hero_class = ctx.classes().get_by_name("CDOTA_Unit_Hero_Axe")?;
36/// println!("Found class: {}", hero_class.name());
37///
38/// // Get by ID
39/// let class = ctx.classes().get_by_id(42)?;
40/// # Ok(())
41/// # }
42/// ```
43#[derive(Default)]
44pub struct Classes {
45 pub(crate) classes_vec: Vec<Rc<Class>>,
46 pub(crate) classes_by_name: HashMap<Box<str>, Rc<Class>>,
47 pub(crate) class_id_size: u32,
48}
49
50impl Classes {
51 pub(crate) fn get_by_id_rc(&self, id: usize) -> &Rc<Class> {
52 &self.classes_vec[id]
53 }
54
55 /// Returns an iterator over all classes.
56 ///
57 /// This allows you to discover all entity types in the replay.
58 pub fn iter(&self) -> impl Iterator<Item = &Class> {
59 self.classes_vec.iter().map(|class| class.as_ref())
60 }
61
62 /// Gets a class by its numeric ID.
63 ///
64 /// # Arguments
65 ///
66 /// * `id` - The numeric class ID
67 ///
68 /// # Errors
69 ///
70 /// Returns [`ClassError::ClassNotFoundById`] if no class with the given ID exists.
71 ///
72 /// # Examples
73 ///
74 /// ```no_run
75 /// use source2_demo::prelude::*;
76 ///
77 /// # fn example(ctx: &Context) -> anyhow::Result<()> {
78 /// let class = ctx.classes().get_by_id(42)?;
79 /// println!("Class name: {}", class.name());
80 /// # Ok(())
81 /// # }
82 /// ```
83 pub fn get_by_id(&self, id: usize) -> Result<&Class, ClassError> {
84 self.classes_vec
85 .get(id)
86 .ok_or(ClassError::ClassNotFoundById(id as i32))
87 .map(|class| class.as_ref())
88 }
89
90 /// Gets a class by its name.
91 ///
92 /// This is the most common way to find entity classes since you typically
93 /// know the class name you're looking for (e.g., "CDOTA_Unit_Hero_Axe").
94 ///
95 /// # Arguments
96 ///
97 /// * `name` - The class name (case-sensitive, e.g., "CDOTA_PlayerResource")
98 ///
99 /// # Errors
100 ///
101 /// Returns [`ClassError::ClassNotFoundByName`] if no class with the given
102 /// name exists.
103 ///
104 /// # Examples
105 ///
106 /// ```no_run
107 /// use source2_demo::prelude::*;
108 ///
109 /// # fn example(ctx: &Context) -> anyhow::Result<()> {
110 /// // Find hero class
111 /// let hero_class = ctx.classes().get_by_name("CDOTA_Unit_Hero_Axe")?;
112 ///
113 /// // Find player resource class
114 /// let resource = ctx.classes().get_by_name("CDOTA_PlayerResource")?;
115 /// # Ok(())
116 /// # }
117 /// ```
118 pub fn get_by_name(&self, name: &str) -> Result<&Class, ClassError> {
119 self.classes_by_name
120 .get(name)
121 .ok_or_else(|| ClassError::ClassNotFoundByName(name.to_string()))
122 .map(|class| class.as_ref())
123 }
124}
125
126/// Entity class definition.
127///
128/// Represents the type and structure of an entity. Classes define:
129/// - Class ID: Numeric identifier
130/// - Class name: Human-readable type name (e.g., "CDOTA_Unit_Hero_Axe")
131/// - Serializer: Defines which properties exist and how they're encoded
132///
133/// # Class Name Patterns
134///
135/// Entity class names follow patterns that help identify their type:
136/// - `CDOTA_Unit_Hero_*` - Dota 2 heroes
137/// - `CDOTA_Unit_*` - Dota 2 NPCs and units
138/// - `CDOTA_Item_*` - Dota 2 item holders
139/// - `CDOTA_*` - Other Dota 2 entities
140/// - `CCitadelPlayerPawn` - Deadlock players
141/// - `CPlayer_*` - CS2 players
142///
143/// # Examples
144///
145/// ## Using class information
146///
147/// ```no_run
148/// use source2_demo::prelude::*;
149///
150/// # fn example(entity: &Entity) {
151/// let class = entity.class();
152/// println!("Entity ID: {}", class.id());
153/// println!("Entity name: {}", class.name());
154///
155/// // Check entity type
156/// if class.name().starts_with("CDOTA_Unit_Hero_") {
157/// println!("This is a hero!");
158/// }
159/// # }
160/// ```
161///
162/// ## Finding all entities of a class type
163///
164/// ```no_run
165/// use source2_demo::prelude::*;
166///
167/// # fn example(ctx: &Context) -> anyhow::Result<()> {
168/// // Find all heroes
169/// let heroes: Vec<&Entity> = ctx.entities()
170/// .iter()
171/// .filter(|e| e.class().name().starts_with("CDOTA_Unit_Hero_"))
172/// .collect();
173/// # Ok(())
174/// # }
175/// ```
176#[derive(Clone, Default)]
177pub struct Class {
178 pub(crate) id: i32,
179 pub(crate) name: Box<str>,
180 pub(crate) serializer: Rc<Serializer>,
181}
182
183impl Class {
184 pub(crate) fn new(id: i32, name: Box<str>, serializer: Rc<Serializer>) -> Self {
185 Class {
186 id,
187 name,
188 serializer,
189 }
190 }
191
192 /// Returns the human-readable name of this class.
193 ///
194 /// Examples: "CDOTA_Unit_Hero_Axe", "CDOTA_PlayerResource"
195 ///
196 /// # Examples
197 ///
198 /// ```no_run
199 /// use source2_demo::prelude::*;
200 ///
201 /// # fn example(class: &Class) {
202 /// match class.name() {
203 /// "CDOTA_PlayerResource" => println!("Found player resource"),
204 /// name if name.starts_with("CDOTA_Unit_Hero_") => println!("Found hero"),
205 /// _ => println!("Other entity type"),
206 /// }
207 /// # }
208 /// ```
209 pub fn name(&self) -> &str {
210 self.name.as_ref()
211 }
212
213 /// Returns the numeric ID of this class.
214 ///
215 /// Each class has a unique numeric ID assigned when the replay is parsed.
216 /// This ID is used internally for efficient entity classification.
217 ///
218 /// # Examples
219 ///
220 /// ```no_run
221 /// use source2_demo::prelude::*;
222 ///
223 /// # fn example(class: &Class) {
224 /// println!("Class ID: {}", class.id());
225 /// # }
226 /// ```
227 pub fn id(&self) -> i32 {
228 self.id
229 }
230}