asr/game_engine/unity/mod.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
//! Support for games using the Unity engine.
//!
//! # Example
//!
//! ```no_run
//! # async fn example(process: asr::Process) {
//! use asr::{
//! future::retry,
//! game_engine::unity::il2cpp::{Module, Version},
//! Address, Address64,
//! };
//!
//! // We first attach to the Mono module. Here we know that the game is using IL2CPP 2020.
//! let module = Module::wait_attach(&process, Version::V2020).await;
//! // We access the .NET DLL that the game code is in.
//! let image = module.wait_get_default_image(&process).await;
//!
//! // We access a class called "Timer" in that DLL.
//! let timer_class = image.wait_get_class(&process, &module, "Timer").await;
//! // We access a static field called "_instance" representing the singleton
//! // instance of the class.
//! let instance = timer_class.wait_get_static_instance(&process, &module, "_instance").await;
//!
//! // Once we have the address of the instance, we want to access one of its
//! // fields, so we get the offset of the "currentTime" field.
//! let current_time_offset = timer_class.wait_get_field_offset(&process, &module, "currentTime").await;
//!
//! // Now we can add it to the address of the instance and read the current time.
//! if let Ok(current_time) = process.read::<f32>(instance + current_time_offset) {
//! // Use the current time.
//! }
//! # }
//! ```
//! Alternatively you can use the `Class` derive macro to generate the bindings
//! for you. This allows reading the contents of an instance of the class
//! described by the struct from a process. Each field must match the name of
//! the field in the class exactly (or alternatively renamed with the `#[rename
//! = "..."]` attribute) and needs to be of a type that can be read from a
//! process. Fields can be marked as static with the `#[static_field]`
//! attribute.
//!
//! ```ignore
//! #[derive(Class)]
//! struct Timer {
//! #[rename = "currentLevelTime"]
//! level_time: f32,
//! #[static_field]
//! foo: bool,
//! }
//! ```
//!
//! This will bind to a .NET class of the following shape:
//!
//! ```csharp
//! class Timer
//! {
//! float currentLevelTime;
//! static bool foo;
//! // ...
//! }
//! ```
//!
//! The class can then be bound to the process like so:
//!
//! ```ignore
//! let timer_class = Timer::bind(&process, &module, &image).await;
//! ```
//!
//! Once you have an instance, you can read the instance from the process like
//! so:
//!
//! ```ignore
//! if let Ok(timer) = timer_class.read(&process, timer_instance) {
//! // Do something with the instance.
//! }
//! ```
//!
//! If only static fields are present, the `read` method does not take an
//! instance argument.
// References:
// https://github.com/just-ero/asl-help/tree/4c87822df0125b027d1af75e8e348c485817592d/src/Unity
// https://github.com/Unity-Technologies/mono
// https://github.com/CryZe/lunistice-auto-splitter/blob/b8c01031991783f7b41044099ee69edd54514dba/asr-dotnet/src/lib.rs
pub mod il2cpp;
pub mod mono;
mod scene;
pub use self::scene::*;