-
Notifications
You must be signed in to change notification settings - Fork 12
Refactor SceneManager / Transform; fix a number of bugs in scene/game object traversal #133
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 3 commits
6c56fed
958fbd8
f575ae4
0152784
25d397b
d078723
c974ef2
e964365
5ab8c87
135f371
305c1c5
440ec35
25f596a
5df5ac0
822dac3
1b3ed11
50be285
1cb0786
03646e0
b54ef8e
8679e33
a24f19e
63776b0
972bb5e
1a29b00
6657b25
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,166 @@ | ||
| use super::{SceneManager, CSTR}; | ||
| use crate::game_engine::unity::{il2cpp, mono}; | ||
| use crate::string::ArrayCString; | ||
| use crate::{Address, Address32, Address64, Error, PointerSize, Process}; | ||
| use core::array; | ||
| use core::mem::MaybeUninit; | ||
|
|
||
| /// Representing a GameObject. From a GameObject, you can get the attached components (includes the | ||
| /// C# scripts). | ||
| #[derive(Clone, Debug)] | ||
| pub struct GameObject { | ||
| pub(super) address: Address, | ||
| } | ||
|
|
||
| impl GameObject { | ||
| /// Get the name of the GameObject. | ||
| pub fn get_name<const N: usize>( | ||
| &self, | ||
| process: &Process, | ||
| scene_manager: &SceneManager, | ||
| ) -> Result<ArrayCString<N>, Error> { | ||
| process.read_pointer_path( | ||
| self.address, | ||
| scene_manager.pointer_size, | ||
| &[scene_manager.offsets.game_object_name as u64, 0x0], | ||
| ) | ||
| } | ||
|
|
||
| /// Traverse the classes associated with the Components attached to this game object. | ||
| pub fn classes<'a>( | ||
| &'a self, | ||
| process: &'a Process, | ||
| scene_manager: &'a SceneManager, | ||
| ) -> Result<impl Iterator<Item = Address> + 'a, Error> { | ||
| let (number_of_components, component_pair_array): (usize, Address) = | ||
| match scene_manager.pointer_size { | ||
| PointerSize::Bit64 => { | ||
| let array = process | ||
| .read::<[Address64; 3]>(self.address + scene_manager.offsets.game_object)?; | ||
| (array[2].value() as usize, array[0].into()) | ||
| } | ||
| _ => { | ||
| let array = process | ||
| .read::<[Address32; 3]>(self.address + scene_manager.offsets.game_object)?; | ||
| (array[2].value() as usize, array[0].into()) | ||
| } | ||
| }; | ||
|
|
||
| if number_of_components == 0 { | ||
| return Err(Error {}); | ||
| } | ||
|
|
||
| const ARRAY_SIZE: usize = 128; | ||
|
|
||
| let components: [Address; ARRAY_SIZE] = match scene_manager.pointer_size { | ||
| PointerSize::Bit64 => { | ||
| let mut buf = [MaybeUninit::<[Address64; 2]>::uninit(); ARRAY_SIZE]; | ||
| let slice = process.read_into_uninit_slice( | ||
| component_pair_array, | ||
| &mut buf[..number_of_components], | ||
| )?; | ||
|
|
||
| let mut iter = slice.iter_mut(); | ||
| array::from_fn(|_| { | ||
| iter.next() | ||
| .map(|&mut [_, second]| second.into()) | ||
| .unwrap_or_default() | ||
| }) | ||
| } | ||
| _ => { | ||
| let mut buf = [MaybeUninit::<[Address32; 2]>::uninit(); ARRAY_SIZE]; | ||
| let slice = process.read_into_uninit_slice( | ||
| component_pair_array, | ||
| &mut buf[..number_of_components], | ||
| )?; | ||
|
|
||
| let mut iter = slice.iter_mut(); | ||
| array::from_fn(|_| { | ||
| iter.next() | ||
| .map(|&mut [_, second]| second.into()) | ||
| .unwrap_or_default() | ||
| }) | ||
| } | ||
| }; | ||
|
|
||
| Ok((1..number_of_components).filter_map(move |m| { | ||
| process | ||
| .read_pointer( | ||
| components[m] + scene_manager.offsets.klass, | ||
| scene_manager.pointer_size, | ||
| ) | ||
| .ok() | ||
| .filter(|val| !val.is_null()) | ||
| })) | ||
| } | ||
|
|
||
| // TODO it's really dumb i have to split this by mono/il2cpp | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we really need to implement some sort of strategy pattern here. The amount of code duplication is getting ridiculous |
||
|
|
||
| /// Tries to find the base address of a class in the current `GameObject` by name. | ||
| /// | ||
| /// Mono only. | ||
| pub fn get_class_mono( | ||
| &self, | ||
| process: &Process, | ||
| scene_manager: &SceneManager, | ||
| module: &mono::Module, | ||
| name: &str, | ||
| ) -> Result<Address, Error> { | ||
| if scene_manager.is_il2cpp { | ||
| return Err(Error {}); | ||
| } | ||
|
|
||
| self.classes(process, scene_manager)? | ||
| .find(|&addr| { | ||
| let val = mono::Class::get_from_component(process, module, addr) | ||
| .and_then(|c| c.get_name::<CSTR>(process, module)); | ||
|
|
||
| val.is_ok_and(|class_name| class_name.matches(name)) | ||
| }) | ||
| .ok_or(Error {}) | ||
| } | ||
|
|
||
| /// Tries to find the base address of a class in the current `GameObject` by name. | ||
| /// | ||
| /// IL2CPP only. | ||
| pub fn get_class_il2cpp( | ||
| &self, | ||
| process: &Process, | ||
| scene_manager: &SceneManager, | ||
| module: &il2cpp::Module, | ||
| name: &str, | ||
| ) -> Result<Address, Error> { | ||
| if !scene_manager.is_il2cpp { | ||
| return Err(Error {}); | ||
| } | ||
|
|
||
| self.classes(process, scene_manager)? | ||
| .find(|&addr| { | ||
| let val = il2cpp::Class::get_from_component(process, module, addr) | ||
| .and_then(|c| c.get_name::<CSTR>(process, module)); | ||
|
|
||
| val.is_ok_and(|class_name| class_name.matches(name)) | ||
| }) | ||
| .ok_or(Error {}) | ||
| } | ||
|
|
||
| /// Returns whether the game object is considered "active" by the scene (if it or any of its | ||
| /// parents are inactive, then the game object is inactive) | ||
| pub fn is_active_in_hierarchy( | ||
| &self, | ||
| process: &Process, | ||
| scene_manager: &SceneManager, | ||
| ) -> Result<bool, Error> { | ||
| process.read::<bool>(self.address + scene_manager.offsets.game_object_activeinhierarchy) | ||
| } | ||
|
|
||
| /// Returns whether the game object is considered "active" by itself (irrespective of any of its | ||
| /// parents) | ||
| pub fn is_active_in_self( | ||
| &self, | ||
| process: &Process, | ||
| scene_manager: &SceneManager, | ||
| ) -> Result<bool, Error> { | ||
| process.read::<bool>(self.address + scene_manager.offsets.game_object_activeself) | ||
| } | ||
| } | ||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,8 +9,9 @@ pub(super) struct Offsets { | |
| pub(super) root_storage_container: u8, | ||
| pub(super) game_object: u8, | ||
| pub(super) game_object_name: u8, | ||
| pub(super) game_object_activeself: u8, | ||
| pub(super) game_object_activeinhierarchy: u8, | ||
| pub(super) klass: u8, | ||
| pub(super) klass_name: u8, | ||
| pub(super) children_pointer: u8, | ||
| } | ||
|
|
||
|
|
@@ -26,8 +27,9 @@ impl Offsets { | |
| root_storage_container: 0xB0, | ||
| game_object: 0x30, | ||
| game_object_name: 0x60, | ||
| game_object_activeself: 0x5E, | ||
| game_object_activeinhierarchy: 0x5F, | ||
| klass: 0x28, | ||
| klass_name: 0x48, | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this changes depending on the mono version, which, of course it does. Now we just store the offset to the object handle, and you have to use mono/il2cpp specific code to get the component |
||
| children_pointer: 0x70, | ||
| }), | ||
| PointerSize::Bit32 => Some(&Self { | ||
|
|
@@ -39,8 +41,9 @@ impl Offsets { | |
| root_storage_container: 0x88, | ||
| game_object: 0x1C, | ||
| game_object_name: 0x3C, | ||
| game_object_activeself: 0x32, | ||
| game_object_activeinhierarchy: 0x33, | ||
| klass: 0x18, | ||
| klass_name: 0x2C, | ||
| children_pointer: 0x50, | ||
| }), | ||
| _ => None, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"component" isn't accurate here, but there's something in between the component and the class. not exactly sure what it is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[ ] TODO: figure out what it is