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 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}