Skip to main content

il2cpp_bridge_rs/structs/components/scene/
scene_management.rs

1//! Unity SceneManager and Scene component wrapper
2use crate::logger;
3use crate::{
4    api::cache,
5    structs::{
6        collections::{Il2cppArray, Il2cppString},
7        components::GameObject,
8        core::Class,
9    },
10};
11use std::ffi::c_void;
12
13#[repr(C)]
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct Scene {
16    /// Handle to the underlying scene
17    pub handle: i32,
18}
19
20impl Scene {
21    /// Gets the Scene class definition
22    fn get_class() -> Option<Class> {
23        cache::coremodule().class("UnityEngine.SceneManagement.Scene")
24    }
25
26    /// Checks if the scene is valid
27    ///
28    /// # Returns
29    /// * `bool` - True if the scene is valid
30    pub fn is_valid(&self) -> bool {
31        unsafe {
32            if let Some(class) = Self::get_class() {
33                if let Some(method) = class.method("IsValidInternal") {
34                    return method
35                        .call::<bool>(&[&self.handle as *const i32 as *mut c_void])
36                        .unwrap_or(false);
37                }
38            }
39            false
40        }
41    }
42
43    /// Gets the build index of the scene
44    ///
45    /// # Returns
46    /// * `i32` - The index in Build Settings, or -1 if not found
47    pub fn build_index(&self) -> i32 {
48        unsafe {
49            if let Some(class) = Self::get_class() {
50                if let Some(method) = class.method("GetBuildIndexInternal") {
51                    return method
52                        .call::<i32>(&[&self.handle as *const i32 as *mut c_void])
53                        .unwrap_or(-1);
54                }
55            }
56            -1
57        }
58    }
59
60    /// Checks if the scene is loaded
61    ///
62    /// # Returns
63    /// * `bool` - True if the scene is currently loaded
64    pub fn is_loaded(&self) -> bool {
65        unsafe {
66            if let Some(class) = Self::get_class() {
67                if let Some(method) = class.method("GetIsLoadedInternal") {
68                    return method
69                        .call::<bool>(&[&self.handle as *const i32 as *mut c_void])
70                        .unwrap_or(false);
71                }
72            }
73            false
74        }
75    }
76
77    /// Gets the name of the scene
78    ///
79    /// # Returns
80    /// * `String` - The name of the scene
81    pub fn name(&self) -> String {
82        unsafe {
83            if let Some(class) = Self::get_class() {
84                if let Some(method) = class.method("GetNameInternal") {
85                    if let Ok(ptr) = method
86                        .call::<*mut Il2cppString>(&[&self.handle as *const i32 as *mut c_void])
87                    {
88                        if !ptr.is_null() {
89                            return (*ptr).to_string().unwrap_or_default();
90                        }
91                    }
92                }
93            }
94            String::new()
95        }
96    }
97
98    /// Gets the path of the scene asset
99    ///
100    /// # Returns
101    /// * `String` - The asset path of the scene
102    pub fn path(&self) -> String {
103        unsafe {
104            if let Some(class) = Self::get_class() {
105                if let Some(method) = class.method("GetPathInternal") {
106                    if let Ok(ptr) = method
107                        .call::<*mut Il2cppString>(&[&self.handle as *const i32 as *mut c_void])
108                    {
109                        if !ptr.is_null() {
110                            return (*ptr).to_string().unwrap_or_default();
111                        }
112                    }
113                }
114            }
115            String::new()
116        }
117    }
118
119    /// Gets the count of root game objects in the scene
120    ///
121    /// # Returns
122    /// * `i32` - The number of root GameObjects
123    pub fn root_count(&self) -> i32 {
124        unsafe {
125            if let Some(class) = Self::get_class() {
126                if let Some(method) = class.method("GetRootCountInternal") {
127                    return method
128                        .call::<i32>(&[&self.handle as *const i32 as *mut c_void])
129                        .unwrap_or(0);
130                }
131            }
132            0
133        }
134    }
135
136    /// Gets all root game objects in the scene
137    ///
138    /// # Returns
139    /// * `Vec<GameObject>` - A list of all root GameObjects
140    pub fn root_game_objects(&self) -> Vec<GameObject> {
141        unsafe {
142            let class = match Self::get_class() {
143                Some(c) => c,
144                None => return Vec::new(),
145            };
146
147            let mut method = match class.method(("GetRootGameObjects", 0)) {
148                Some(m) => m,
149                None => {
150                    logger::error("Method 'GetRootGameObjects' with 0 args not found");
151                    return Vec::new();
152                }
153            };
154
155            method.instance = Some(&self.handle as *const i32 as *mut c_void);
156
157            let ptr = method
158                .call::<*mut Il2cppArray<*mut c_void>>(&[])
159                .unwrap_or(std::ptr::null_mut());
160
161            if ptr.is_null() {
162                return Vec::new();
163            }
164
165            let array_ptr = ptr;
166            let mut objects = Vec::new();
167            let array = &*array_ptr;
168
169            for i in 0..array.max_length {
170                let obj_ptr = array.at(i);
171                if !obj_ptr.is_null() {
172                    objects.push(GameObject::from_ptr(obj_ptr));
173                }
174            }
175            objects
176        }
177    }
178}
179
180pub struct SceneManager;
181
182impl SceneManager {
183    /// Gets the SceneManager class definition
184    fn get_class() -> Option<Class> {
185        cache::coremodule().class("UnityEngine.SceneManagement.SceneManager")
186    }
187
188    /// Gets the total number of scenes
189    ///
190    /// # Returns
191    /// * `i32` - The number of scenes currently loaded
192    pub fn scene_count() -> i32 {
193        unsafe {
194            if let Some(class) = Self::get_class() {
195                if let Some(method) = class.method("get_sceneCount") {
196                    return method.call::<i32>(&[]).unwrap_or(0);
197                }
198            }
199            0
200        }
201    }
202
203    /// Gets the currently active scene
204    ///
205    /// # Returns
206    /// * `Result<Scene, String>` - The active scene
207    pub fn get_active_scene() -> Result<Scene, String> {
208        let class = Self::get_class()
209            .ok_or("Class 'UnityEngine.SceneManagement.SceneManager' not found")?;
210
211        let method = class
212            .method("GetActiveScene")
213            .ok_or("Method 'GetActiveScene' not found")?;
214
215        unsafe { method.call::<Scene>(&[]) }
216    }
217
218    /// Gets the scene at the specified index
219    ///
220    /// # Arguments
221    /// * `index` - The index of the scene to retrieve
222    ///
223    /// # Returns
224    /// * `Result<Scene, String>` - The requested scene
225    pub fn get_scene_at(index: i32) -> Result<Scene, String> {
226        let class = Self::get_class()
227            .ok_or("Class 'UnityEngine.SceneManagement.SceneManager' not found")?;
228
229        let method = class
230            .method("GetSceneAt")
231            .ok_or("Method 'GetSceneAt' not found")?;
232
233        unsafe { method.call::<Scene>(&[&index as *const i32 as *mut c_void]) }
234    }
235
236    /// Gets a scene by name
237    ///
238    /// # Arguments
239    /// * `name` - The name of the scene to find
240    ///
241    /// # Returns
242    /// * `Result<Scene, String>` - The requested scene
243    pub fn get_scene_by_name(name: &str) -> Result<Scene, String> {
244        let class = Self::get_class()
245            .ok_or("Class 'UnityEngine.SceneManagement.SceneManager' not found")?;
246
247        let method = class
248            .method("GetSceneByName")
249            .ok_or("Method 'GetSceneByName' not found")?;
250
251        let name_str = Il2cppString::new(name);
252        if name_str.is_null() {
253            return Err("Failed to create Il2cppString".to_string());
254        }
255
256        unsafe { method.call::<Scene>(&[name_str as *mut c_void]) }
257    }
258
259    /// Loads the scene at the specified index
260    ///
261    /// # Arguments
262    /// * `index` - The build index of the scene to load
263    ///
264    /// # Returns
265    /// * `Result<(), String>` - Ok if success
266    pub fn load_scene_at(index: i32) -> Result<(), String> {
267        let class = Self::get_class()
268            .ok_or("Class 'UnityEngine.SceneManagement.SceneManager' not found")?;
269
270        let method = class
271            .method(("LoadScene", ["System.Int32"]))
272            .ok_or("Method 'LoadScene(int)' not found")?;
273
274        unsafe {
275            method.call::<()>(&[&index as *const i32 as *mut c_void])?;
276        }
277        Ok(())
278    }
279
280    /// Loads the scene by name
281    ///
282    /// # Arguments
283    /// * `name` - The name or path of the scene to load
284    ///
285    /// # Returns
286    /// * `Result<(), String>` - Ok if success
287    pub fn load_scene_name(name: &str) -> Result<(), String> {
288        let class = Self::get_class()
289            .ok_or("Class 'UnityEngine.SceneManagement.SceneManager' not found")?;
290
291        let method = class
292            .method(("LoadScene", ["System.String"]))
293            .ok_or("Method 'LoadScene(string)' not found")?;
294
295        let name_str = Il2cppString::new(name);
296        if name_str.is_null() {
297            return Err("Failed to create Il2cppString".to_string());
298        }
299
300        unsafe {
301            method.call::<()>(&[name_str as *mut c_void])?;
302        }
303        Ok(())
304    }
305}