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
71 /// exists.
72 ///
73 /// # Examples
74 ///
75 /// ```no_run
76 /// use source2_demo::Context;
77 ///
78 /// # fn example(ctx: &Context) -> anyhow::Result<()> {
79 /// let class = ctx.classes().get_by_id(42)?;
80 /// println!("Class name: {}", class.name());
81 /// # Ok(())
82 /// # }
83 /// ```
84 pub fn get_by_id(&self, id: usize) -> Result<&Class, ClassError> {
85 self.classes_vec
86 .get(id)
87 .ok_or(ClassError::ClassNotFoundById(id as i32))
88 .map(|class| class.as_ref())
89 }
90
91 /// Gets a class by its name.
92 ///
93 /// This is the most common way to find entity classes since you typically
94 /// know the class name you're looking for (e.g., "CDOTA_Unit_Hero_Axe").
95 ///
96 /// # Arguments
97 ///
98 /// * `name` - The class name (case-sensitive, e.g., "CDOTA_PlayerResource")
99 ///
100 /// # Errors
101 ///
102 /// Returns [`ClassError::ClassNotFoundByName`] if no class with the given
103 /// name exists.
104 ///
105 /// # Examples
106 ///
107 /// ```no_run
108 /// use source2_demo::Context;
109 ///
110 /// # fn example(ctx: &Context) -> anyhow::Result<()> {
111 /// // Find hero class
112 /// let hero_class = ctx.classes().get_by_name("CDOTA_Unit_Hero_Axe")?;
113 ///
114 /// // Find player resource class
115 /// let resource = ctx.classes().get_by_name("CDOTA_PlayerResource")?;
116 /// # Ok(())
117 /// # }
118 /// ```
119 pub fn get_by_name(&self, name: &str) -> Result<&Class, ClassError> {
120 self.classes_by_name
121 .get(name)
122 .ok_or_else(|| ClassError::ClassNotFoundByName(name.to_string()))
123 .map(|class| class.as_ref())
124 }
125}
126
127/// Entity class definition.
128///
129/// Represents the type and structure of an entity. Classes define:
130/// - Class ID: Numeric identifier
131/// - Class name: Human-readable type name (e.g., "CDOTA_Unit_Hero_Axe")
132/// - Serializer: Defines which properties exist and how they're encoded
133///
134/// # Class Name Patterns
135///
136/// Entity class names follow patterns that help identify their type:
137/// - `CDOTA_Unit_Hero_*` - Dota 2 heroes
138/// - `CDOTA_Unit_*` - Dota 2 NPCs and units
139/// - `CDOTA_Item_*` - Dota 2 item holders
140/// - `CDOTA_*` - Other Dota 2 entities
141/// - `CCitadelPlayerPawn` - Deadlock players
142/// - `CPlayer_*` - CS2 players
143///
144/// # Examples
145///
146/// ## Using class information
147///
148/// ```no_run
149/// use source2_demo::prelude::*;
150///
151/// # fn example(entity: &Entity) {
152/// let class = entity.class();
153/// println!("Entity ID: {}", class.id());
154/// println!("Entity name: {}", class.name());
155///
156/// // Check entity type
157/// if class.name().starts_with("CDOTA_Unit_Hero_") {
158/// println!("This is a hero!");
159/// }
160/// # }
161/// ```
162///
163/// ## Finding all entities of a class type
164///
165/// ```no_run
166/// use source2_demo::prelude::*;
167///
168/// # fn example(ctx: &Context) -> anyhow::Result<()> {
169/// // Find all heroes
170/// let heroes: Vec<&Entity> = ctx
171/// .entities()
172/// .iter()
173/// .filter(|e| e.class().name().starts_with("CDOTA_Unit_Hero_"))
174/// .collect();
175/// # Ok(())
176/// # }
177/// ```
178#[derive(Clone, Default)]
179pub struct Class {
180 pub(crate) id: i32,
181 pub(crate) name: Box<str>,
182 pub(crate) serializer: Rc<Serializer>,
183}
184
185impl Class {
186 pub(crate) fn new(id: i32, name: Box<str>, serializer: Rc<Serializer>) -> Self {
187 Class {
188 id,
189 name,
190 serializer,
191 }
192 }
193
194 /// Returns the human-readable name of this class.
195 ///
196 /// Examples: "CDOTA_Unit_Hero_Axe", "CDOTA_PlayerResource"
197 ///
198 /// # Examples
199 ///
200 /// ```no_run
201 /// use source2_demo::Class;
202 ///
203 /// # fn example(class: &Class) {
204 /// match class.name() {
205 /// "CDOTA_PlayerResource" => println!("Found player resource"),
206 /// name if name.starts_with("CDOTA_Unit_Hero_") => println!("Found hero"),
207 /// _ => println!("Other entity type"),
208 /// }
209 /// # }
210 /// ```
211 pub fn name(&self) -> &str {
212 self.name.as_ref()
213 }
214
215 /// Returns the numeric ID of this class.
216 ///
217 /// Each class has a unique numeric ID assigned when the replay is parsed.
218 /// This ID is used internally for efficient entity classification.
219 ///
220 /// # Examples
221 ///
222 /// ```no_run
223 /// use source2_demo::Class;
224 ///
225 /// # fn example(class: &Class) {
226 /// println!("Class ID: {}", class.id());
227 /// # }
228 /// ```
229 pub fn id(&self) -> i32 {
230 self.id
231 }
232}