Skip to main content

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}