diff --git a/Cargo.toml b/Cargo.toml index 7a58de43..abb54614 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,8 @@ resolver = "2" members = [ "apps/testing", "apps/desktop", - "packages/core" + "packages/core", + "packages/macros" ] [workspace.package] @@ -21,6 +22,7 @@ version = "1.0.0-alpha.1" [workspace.dependencies] onelauncher = { path = "./packages/core" } +onelauncher_macros = { path = "./packages/macros" } # tauri uses latest crates.io beta channel (temporarily can use v2 and main branches if necessary) tauri = { version = "=2.0.0-beta.23", features = [ diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index b83d347e..3eb6d94c 100644 --- a/packages/core/Cargo.toml +++ b/packages/core/Cargo.toml @@ -23,6 +23,8 @@ specta = [ "dep:specta" ] cli = [ "dep:indicatif" ] [dependencies] +onelauncher_macros = { workspace = true } + # GUI-only deps tauri = { workspace = true, optional = true } tauri-specta = { workspace = true, optional = true } diff --git a/packages/core/src/api/cluster/create.rs b/packages/core/src/api/cluster/create.rs index 1292b4ca..1ff0a7d0 100644 --- a/packages/core/src/api/cluster/create.rs +++ b/packages/core/src/api/cluster/create.rs @@ -13,6 +13,7 @@ use std::path::PathBuf; /// Creates a [`Cluster`] and adds it to the memory [`State`]. /// Returns a relative filepath ([`ClusterPath`]) which can be used to access the cluster. #[tracing::instrument] +#[onelauncher_macros::memory] #[allow(clippy::too_many_arguments)] pub async fn create_cluster( mut name: String, @@ -206,6 +207,7 @@ pub async fn create_cluster_from_duplicate(from: ClusterPath) -> crate::Result crate::Result /// run a Minecraft [`Cluster`] using [`MinecraftCredentials`] for authentication. /// returns an [`Arc`] pointer to [`RwLock`] to [`ProcessorChild`]. #[tracing::instrument(skip(creds))] +#[onelauncher_macros::memory] pub async fn run_credentials( path: &ClusterPath, creds: &MinecraftCredentials, @@ -358,6 +359,7 @@ pub async fn get_optimal_java_version(path: &ClusterPath) -> crate::Result crate::Result<()> { let state = State::get().await?; let cluster = get(path, None) diff --git a/packages/core/src/api/java.rs b/packages/core/src/api/java.rs index e666d498..52136ffb 100644 --- a/packages/core/src/api/java.rs +++ b/packages/core/src/api/java.rs @@ -27,6 +27,7 @@ pub async fn filter_java_version(java_version: Option) -> crate::Result crate::Result { let state = State::get().await?; diff --git a/packages/core/src/api/package/from.rs b/packages/core/src/api/package/from.rs index f5a128d4..9541b705 100644 --- a/packages/core/src/api/package/from.rs +++ b/packages/core/src/api/package/from.rs @@ -185,6 +185,7 @@ pub fn get_cluster_from_pack(location: CreatePackLocation) -> CreatePackCluster } #[tracing::instrument] +#[onelauncher_macros::memory] pub async fn generate_pack_from_version_id( package_id: String, version_id: String, @@ -294,6 +295,7 @@ pub async fn generate_pack_from_version_id( } #[tracing::instrument] +#[onelauncher_macros::memory] pub async fn generate_pack_from_file( path: PathBuf, cluster_path: ClusterPath, @@ -314,6 +316,7 @@ pub async fn generate_pack_from_file( /// Sets generated cluster attributes to the pack attributes (using `cluster::edit`). /// This includes the pack name, icon, game version, loader version, and loader. +#[onelauncher_macros::memory] pub async fn set_cluster_information( cluster_path: ClusterPath, description: &CreatePackDescription, diff --git a/packages/core/src/api/package/import/atlauncher.rs b/packages/core/src/api/package/import/atlauncher.rs index b3d48188..79689496 100644 --- a/packages/core/src/api/package/import/atlauncher.rs +++ b/packages/core/src/api/package/import/atlauncher.rs @@ -113,6 +113,7 @@ pub async fn is_valid_atlauncher(instance_folder: PathBuf) -> bool { } #[tracing::instrument] +#[onelauncher_macros::memory] pub async fn import_atlauncher( // The path to the base ATLauncher folder. atlauncher_base_path: PathBuf, diff --git a/packages/core/src/api/package/import/mod.rs b/packages/core/src/api/package/import/mod.rs index 814794db..a737716c 100644 --- a/packages/core/src/api/package/import/mod.rs +++ b/packages/core/src/api/package/import/mod.rs @@ -107,6 +107,7 @@ pub async fn import_instances(import: ImportType, path: PathBuf) -> crate::Resul } #[tracing::instrument] +#[onelauncher_macros::memory] pub async fn import_instance( cluster_path: ClusterPath, import: ImportType, @@ -184,6 +185,7 @@ pub fn default_launcher_path(r#type: ImportType) -> Option { /// Checks if a [`PathBuf`] is a valid instance for a given [`ImportType`] #[tracing::instrument] +#[onelauncher_macros::memory] pub async fn is_valid_instance(instance_path: PathBuf, r#type: ImportType) -> bool { match r#type { ImportType::MultiMC | ImportType::PrismLauncher => { @@ -202,6 +204,7 @@ pub async fn is_valid_instance(instance_path: PathBuf, r#type: ImportType) -> bo } #[tracing::instrument] +#[onelauncher_macros::memory] pub async fn cache_icon(icon_path: PathBuf) -> crate::Result> { let state = crate::State::get().await?; let bytes = tokio::fs::read(&icon_path).await; @@ -251,6 +254,7 @@ pub async fn copy_minecraft( } /// recursively get a [`Vec`] of all subfiles. +#[onelauncher_macros::memory] #[async_recursion::async_recursion] #[tracing::instrument] pub async fn sub(path: &Path) -> crate::Result> { diff --git a/packages/core/src/api/package/import/multibased.rs b/packages/core/src/api/package/import/multibased.rs index 379ec917..89f6738c 100644 --- a/packages/core/src/api/package/import/multibased.rs +++ b/packages/core/src/api/package/import/multibased.rs @@ -169,6 +169,7 @@ async fn load_instance_cfg(file_path: &Path) -> crate::Result { } #[tracing::instrument] +#[onelauncher_macros::memory] pub async fn import_mmc( mmc_base_path: PathBuf, instance_folder: String, diff --git a/packages/core/src/api/package/mrpack.rs b/packages/core/src/api/package/mrpack.rs index cc285e92..7d59fd28 100644 --- a/packages/core/src/api/package/mrpack.rs +++ b/packages/core/src/api/package/mrpack.rs @@ -25,6 +25,7 @@ use super::from::{ /// Wrapper around [`install_pack_files`] that generates a pack creation description, and /// attempts to install the pack files. If it fails, it will remove the cluster (fail safely) /// Install a modpack from a mrpack file (a modrinth .zip format) +#[onelauncher_macros::memory] pub async fn install_zipped_mrpack( location: CreatePackLocation, cluster_path: ClusterPath, @@ -65,6 +66,7 @@ pub async fn install_zipped_mrpack( } /// Install all pack files from a description. Does not remove the cluster if it fails +#[onelauncher_macros::memory] pub async fn install_zipped_mrpack_files( create_pack: CreatePack, ignore_lock: bool, @@ -239,6 +241,7 @@ pub async fn install_zipped_mrpack_files( } #[tracing::instrument(skip(mrpack_file))] +#[onelauncher_macros::memory] pub async fn remove_all_related_files( cluster_path: ClusterPath, mrpack_file: bytes::Bytes, diff --git a/packages/core/src/api/proxy/send.rs b/packages/core/src/api/proxy/send.rs index 0e961c12..656be912 100644 --- a/packages/core/src/api/proxy/send.rs +++ b/packages/core/src/api/proxy/send.rs @@ -11,6 +11,7 @@ use crate::api::proxy::{ClusterPayload, IngressPayload, MessagePayload, ProcessP #[cfg(feature = "tauri")] use tauri::Manager; +#[onelauncher_macros::memory] pub async fn init_ingress( ingress_type: IngressType, total: f64, @@ -24,6 +25,7 @@ pub async fn init_ingress( #[cfg(feature = "cli")] const CLI_TOTAL_INGRESS: u64 = 1000; +#[onelauncher_macros::memory] pub async fn init_ingress_internal( ingress_type: IngressType, total: f64, @@ -98,6 +100,7 @@ pub async fn edit_ingress( #[allow(unused_variables)] #[tracing::instrument(level = "debug")] +#[onelauncher_macros::memory] pub async fn send_ingress( key: &IngressId, increment: f64, diff --git a/packages/core/src/api/proxy/utils.rs b/packages/core/src/api/proxy/utils.rs index f7379356..4f37b625 100644 --- a/packages/core/src/api/proxy/utils.rs +++ b/packages/core/src/api/proxy/utils.rs @@ -45,6 +45,7 @@ macro_rules! ingress_join { /// TryStreamExt concurrently for Ingress feeds #[tracing::instrument(skip(stream, f))] +#[onelauncher_macros::memory] pub async fn ingress_try_for_each( stream: I, limit: Option, diff --git a/packages/core/src/game/metadata.rs b/packages/core/src/game/metadata.rs index c4f1b960..b58f7e57 100644 --- a/packages/core/src/game/metadata.rs +++ b/packages/core/src/game/metadata.rs @@ -50,6 +50,7 @@ pub async fn download_minecraft( } #[tracing::instrument(skip_all, fields(version = version.id.as_str(), loader = ?loader))] +#[onelauncher_macros::memory] pub async fn download_version_info( st: &State, version: &GameVersion, @@ -112,6 +113,7 @@ pub async fn fetch_partial_version( } #[tracing::instrument(skip_all)] +#[onelauncher_macros::memory] pub async fn download_assets_index( st: &State, version: &GameVersionInfo, @@ -160,6 +162,7 @@ pub async fn fetch_assets_index( } #[tracing::instrument(skip(st, index))] +#[onelauncher_macros::memory] pub async fn download_assets( st: &State, legacy: bool, @@ -221,6 +224,7 @@ pub async fn download_assets( } #[tracing::instrument(skip(st, libraries))] +#[onelauncher_macros::memory] #[allow(clippy::too_many_arguments)] pub async fn download_libraries( st: &State, @@ -315,6 +319,7 @@ pub async fn download_libraries( } #[tracing::instrument(skip_all)] +#[onelauncher_macros::memory] pub async fn download_client( st: &State, version: &GameVersionInfo, diff --git a/packages/core/src/game/mod.rs b/packages/core/src/game/mod.rs index 7d319d8c..59809151 100644 --- a/packages/core/src/game/mod.rs +++ b/packages/core/src/game/mod.rs @@ -36,6 +36,7 @@ macro_rules! data_entry { } #[tracing::instrument(skip(cluster))] +#[onelauncher_macros::memory] pub async fn install_minecraft( cluster: &Cluster, old_ingress: Option, @@ -242,6 +243,7 @@ pub async fn install_minecraft( } #[tracing::instrument(skip_all)] +#[onelauncher_macros::memory] #[allow(clippy::too_many_arguments)] pub async fn launch_minecraft( cluster: &Cluster, diff --git a/packages/core/src/store/clusters.rs b/packages/core/src/store/clusters.rs index 61be7b2e..78499cd2 100644 --- a/packages/core/src/store/clusters.rs +++ b/packages/core/src/store/clusters.rs @@ -512,6 +512,7 @@ impl Cluster { /// watch the filesystem for changes with [`notify`]. #[tracing::instrument(skip(watcher))] + #[onelauncher_macros::memory] pub async fn watch( cluster_path: &Path, watcher: &mut Debouncer, @@ -542,6 +543,7 @@ impl Cluster { impl Clusters { /// Initialize the cluster manager and HashMap. #[tracing::instrument(skip(watcher))] + #[onelauncher_macros::memory] pub async fn initialize( dirs: &Directories, watcher: &mut Debouncer, @@ -578,6 +580,7 @@ impl Clusters { /// update registered packages #[tracing::instrument] + #[onelauncher_macros::memory] pub async fn update_packages() { let res = async { let state = State::get().await?; @@ -631,6 +634,7 @@ impl Clusters { /// update all available package versions #[tracing::instrument] + #[onelauncher_macros::memory] pub async fn update_versions() { let res = async { let state = State::get().await?; @@ -666,6 +670,7 @@ impl Clusters { /// insert a cluster into the HashMap #[tracing::instrument(skip(self, cluster))] + #[onelauncher_macros::memory] pub async fn insert(&mut self, cluster: Cluster, dont_watch: bool) -> crate::Result<&Self> { send_cluster( cluster.uuid, diff --git a/packages/core/src/store/metadata.rs b/packages/core/src/store/metadata.rs index dcaf2fa7..4d7e721b 100644 --- a/packages/core/src/store/metadata.rs +++ b/packages/core/src/store/metadata.rs @@ -144,6 +144,7 @@ impl Metadata { /// Initialize the core Metadata manager. #[tracing::instrument(skip(io_semaphore, fetch_semaphore))] + #[onelauncher_macros::memory] pub async fn initialize( dirs: &Directories, online: bool, diff --git a/packages/core/src/store/mod.rs b/packages/core/src/store/mod.rs index 538b3e0b..4a7f33f3 100644 --- a/packages/core/src/store/mod.rs +++ b/packages/core/src/store/mod.rs @@ -110,6 +110,7 @@ impl State { /// Initializes the core OneLauncher state fully. #[tracing::instrument] + #[onelauncher_macros::memory] async fn initialize() -> crate::Result> { let ingress = init_ingress_internal(crate::IngressType::Initialize, 100.0, "initializing state") @@ -202,6 +203,7 @@ impl State { /// Synchronizes data that can change outside of our control. #[tracing::instrument] + #[onelauncher_macros::memory] pub async fn sync() -> crate::Result<()> { let state = Self::get().await?; let sync_settings = async { diff --git a/packages/core/src/store/package.rs b/packages/core/src/store/package.rs index 36c0ba46..ab337bd2 100644 --- a/packages/core/src/store/package.rs +++ b/packages/core/src/store/package.rs @@ -21,6 +21,7 @@ use super::{Cluster, PackagePath}; /// Creates [`Package`] data for a given [`Cluster`] from on-device files and APIs. /// Paths must be the full paths and not relative paths. #[tracing::instrument(skip(paths, _cluster, _io_semaphore, _fetch_semaphore))] +#[onelauncher_macros::memory] pub async fn generate_context( _cluster: Cluster, paths: Vec, @@ -323,6 +324,7 @@ pub enum PackageFile { } #[tracing::instrument(skip(io_semaphore))] +#[onelauncher_macros::memory] async fn read_icon( icon_path: Option, cache_path: &Path, diff --git a/packages/core/src/store/processor.rs b/packages/core/src/store/processor.rs index 423d80c7..88be298d 100644 --- a/packages/core/src/store/processor.rs +++ b/packages/core/src/store/processor.rs @@ -412,6 +412,7 @@ impl Processor { /// insert a process with a command and [`ClusterPath`]. #[tracing::instrument(skip(self, uuid, command, post, censors))] #[tracing::instrument(level = "trace", skip(self))] + #[onelauncher_macros::memory] pub async fn insert_process( &mut self, uuid: Uuid, @@ -455,6 +456,7 @@ impl Processor { /// Insert a cached process with a [`ProcessorCache`]. #[tracing::instrument(skip(self, cache))] #[tracing::instrument(level = "trace", skip(self))] + #[onelauncher_macros::memory] pub async fn insert_restored( &mut self, cache: ProcessorCache, @@ -542,6 +544,7 @@ impl Processor { /// Get a process manager and runner awaiting for the exit status. #[tracing::instrument(skip(current_child))] + #[onelauncher_macros::memory] async fn manager( uuid: Uuid, post: Option, diff --git a/packages/core/src/utils/http.rs b/packages/core/src/utils/http.rs index ca7ea9cf..c15e9c42 100644 --- a/packages/core/src/utils/http.rs +++ b/packages/core/src/utils/http.rs @@ -76,6 +76,7 @@ where /// Advanced HTTP fetch interface with json, sha1 validation, and ingress support. #[tracing::instrument(skip(json_body, semaphore))] +#[onelauncher_macros::memory] pub async fn fetch_advanced( method: Method, url: &str, @@ -166,6 +167,7 @@ pub async fn fetch_advanced( /// A utility to fetch from multiple mirrored sources. #[tracing::instrument(skip(semaphore))] +#[onelauncher_macros::memory] pub async fn fetch_from_mirrors( mirrors: &[&str], sha1: Option<&str>, @@ -187,6 +189,7 @@ pub async fn fetch_from_mirrors( /// Checks if we are playing offline by contacting a reliable server. #[tracing::instrument] +#[onelauncher_macros::memory] pub async fn check_internet_connection(timeout: u64) -> bool { REQWEST_CLIENT .get("https://api.polyfrost.org/") @@ -196,8 +199,9 @@ pub async fn check_internet_connection(timeout: u64) -> bool { .is_ok() } -/// Post JSON to a specified URL (helpful for Microsoft auth) +/// Post JSON to a specified URL (helpful for Microsoft auth and Metadata) #[tracing::instrument(skip(json_body, semaphore))] +#[onelauncher_macros::memory] pub async fn post_json( url: &str, json_body: serde_json::Value, diff --git a/packages/core/src/utils/java.rs b/packages/core/src/utils/java.rs index 22c6cb5f..e9d78861 100644 --- a/packages/core/src/utils/java.rs +++ b/packages/core/src/utils/java.rs @@ -183,6 +183,7 @@ pub async fn locate_java() -> Result, JavaError> { /// Locate JRE's auto-installed by the launcher instance. #[tracing::instrument] +#[onelauncher_macros::memory] async fn locate_installed_java() -> Result, JavaError> { Box::pin(async move { let state = State::get().await.map_err(|_| JavaError::MutexError)?; @@ -244,6 +245,7 @@ pub async fn check_java(paths: HashSet) -> HashSet { /// Verifies that a java instance [`Path`] is valid. /// java_check from https://github.com/modrinth/theseus/blob/master/theseus/library/JavaInfo.class under MIT #[tracing::instrument] +#[onelauncher_macros::memory] pub async fn check_java_instance(path: &Path) -> Option { let Ok(path) = io::canonicalize(path) else { return None; diff --git a/packages/macros/Cargo.toml b/packages/macros/Cargo.toml new file mode 100644 index 00000000..b6417147 --- /dev/null +++ b/packages/macros/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "onelauncher_macros" +description = "Debugger macros for OneLauncher" +version = { workspace = true } +license = { workspace = true } +edition = { workspace = true } +repository = { workspace = true } +documentation = { workspace = true } +readme = { workspace = true } +homepage = { workspace = true } +authors = { workspace = true } + +[lib] +proc-macro = true + +[dependencies] +syn = { workspace = true } +quote = { workspace = true } diff --git a/packages/macros/src/lib.rs b/packages/macros/src/lib.rs new file mode 100644 index 00000000..72fd14a0 --- /dev/null +++ b/packages/macros/src/lib.rs @@ -0,0 +1,36 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, ItemFn}; + +/// Procedural macro that isolates a [`Box`] to ensure proper memory allocation in +/// debug environments. +#[proc_macro_attribute] +pub fn memory(_attr: TokenStream, item: TokenStream) -> TokenStream { + let input = parse_macro_input!(item as ItemFn); + + let attrs = &input.attrs; + let vis = &input.vis; + let sig = &input.sig; + let body = &input.block; + + #[cfg(debug_assertions)] + let result = quote! { + #(#attrs)* + #vis #sig { + Box::pin(async move { + #body + }).await + } + }; + #[cfg(not(debug_assertions))] + let result = quote! { + #(#attrs)* + #vis #sig { + #body + } + }; + + TokenStream::from(result) +}