diff --git a/Cargo.lock b/Cargo.lock
index 879900d7..00dc8f33 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3116,6 +3116,7 @@ name = "onelauncher_migration"
version = "0.2.0-alpha.2"
dependencies = [
"async-trait",
+ "chrono",
"sea-orm-migration",
"tokio",
]
diff --git a/Cargo.toml b/Cargo.toml
index 673465c0..48eadd5c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,22 +1,22 @@
[workspace]
-resolver = "2" # virtual workspace resolver so we don't need a root package
members = [
"packages/core",
"packages/entity",
"packages/gamemode",
"packages/macro",
"packages/migration",
- "packages/serde_json_path_to_error"
+ "packages/serde_json_path_to_error",
]
+resolver = "2" # virtual workspace resolver so we don't need a root package
[workspace.package]
-license = "GPL-3.0-only"
-edition = "2024"
-repository = "https://github.com/Polyfrost/OneLauncher"
-documentation = "https://docs.polyfrost.org/onelauncher"
-readme = "README.md"
-homepage = "https://polyfrost.org/projects/onelauncher"
authors = [ "Polyfrost", "Polyfrost Contributors" ]
+documentation = "https://docs.polyfrost.org/onelauncher"
+edition = "2024"
+homepage = "https://polyfrost.org/projects/onelauncher"
+license = "GPL-3.0-only"
+readme = "README.md"
+repository = "https://github.com/Polyfrost/OneLauncher"
version = "0.2.0-alpha.2"
[workspace.dependencies]
@@ -34,16 +34,16 @@ tauri = { version = "2.0", features = [
"tracing",
"specta",
"uuid",
- "protocol-asset"
+ "protocol-asset",
] }
tauri-build = { version = "2.0" }
-tauri-plugin-shell = { version = "2.0" }
-tauri-plugin-window-state = { version = "2.0" }
-tauri-plugin-single-instance = { version = "2.0" }
tauri-plugin-clipboard-manager = { version = "2.0" }
-tauri-plugin-updater = { version = "2.0" }
-tauri-plugin-dialog = { version = "2.0" }
tauri-plugin-deep-link = { version = "2.0" }
+tauri-plugin-dialog = { version = "2.0" }
+tauri-plugin-shell = { version = "2.0" }
+tauri-plugin-single-instance = { version = "2.0" }
+tauri-plugin-updater = { version = "2.0" }
+tauri-plugin-window-state = { version = "2.0" }
# core
interpulse = { version = "=1.2.2", features = [ "specta" ] }
@@ -56,10 +56,10 @@ specta = { version = "2.0.0-rc.20", features = [
"serde_json",
"function",
"derive",
- "time"
+ "time",
] }
-tauri-specta = { version = "2.0.0-rc.20", features = [ "javascript", "typescript", "derive" ] }
specta-typescript = { version = "=0.0.7" }
+tauri-specta = { version = "2.0.0-rc.20", features = [ "javascript", "typescript", "derive" ] }
# security
iota_stronghold = { version = "2.1" }
@@ -67,27 +67,28 @@ zeroize = { version = "1.8", features = [ "zeroize_derive" ] }
# logging
tracing = { version = "0.1" }
-tracing-subscriber = { version = "0.3", features = [ "chrono", "env-filter" ] }
-tracing-error = { version = "0.2" }
tracing-appender = { version = "0.2" }
+tracing-error = { version = "0.2" }
+tracing-subscriber = { version = "0.3", features = [ "chrono", "env-filter" ] }
# serialization & text
+base64 = { version = "0.22" }
+byteorder = { version = "1.5.0" }
+chrono = { version = "0.4", features = [ "serde" ] }
+nom = { version = "7.1" }
+p256 = { version = "0.13.2", features = [ "ecdsa" ] }
+rand = { version = "0.8.5" }
+regex = { version = "1.10" }
serde = { version = "1.0", features = [ "derive" ] }
serde_json = { version = "1.0" }
serde_with = { version = "=3.12.0", features = [ "chrono_0_4", "json", "macros" ] }
-regex = { version = "1.10" }
-url = { version = "2.5.2" }
strum = { version = "0.26", features = [ "derive" ] }
-chrono = { version = "0.4", features = [ "serde" ] }
-rand = { version = "0.8.5" }
-byteorder = { version = "1.5.0" }
-base64 = { version = "0.22" }
-p256 = { version = "0.13.2", features = [ "ecdsa" ] }
-uuid = { version = "1.10", features = [ "serde", "v4" ] }
toml = { version = "0.8" }
-nom = { version = "7.1" }
+url = { version = "2.5.2" }
+uuid = { version = "1.10", features = [ "serde", "v4" ] }
# network
+async-tungstenite = { version = "0.28", features = [ "tokio-runtime", "tokio-rustls-webpki-roots" ] }
reqwest = { version = "0.12", default-features = false, features = [
"json",
"stream",
@@ -99,33 +100,32 @@ reqwest = { version = "0.12", default-features = false, features = [
"macos-system-configuration",
"deflate",
"gzip",
- "brotli"
+ "brotli",
] }
-async-tungstenite = { version = "0.28", features = [ "tokio-runtime", "tokio-rustls-webpki-roots" ] }
webbrowser = { version = "1.0" }
# io
-tokio = { version = "1.40", features = [ "full" ] }
-tokio-util = { version = "0.7", features = [ "compat" ] }
-tokio-stream = { version = "0.1.16", features = [ "fs" ] }
+async-compression = { version = "0.4.12", features = [ "flate2", "tokio" ] }
+async_zip = { version = "0.0.17", features = [ "full" ] }
+bytes = { version = "1.7" }
+dirs = { version = "5.0" }
+dunce = { version = "1.0.5" }
+flate2 = { version = "1.0" }
notify = { version = "6.1", default-features = false }
notify-debouncer-mini = { version = "0.4", default-features = false }
-tempfile = { version = "3.19" }
-dunce = { version = "1.0.5" }
-zip = { version = "2.2.0" }
-dirs = { version = "5.0" }
-async_zip = { version = "0.0.17", features = [ "full" ] }
-async-compression = { version = "0.4.12", features = [ "flate2", "tokio" ] }
-flate2 = { version = "1.0" }
-bytes = { version = "1.7" }
sanitize-filename = { version = "0.6" }
+tempfile = { version = "3.19" }
+tokio = { version = "1.40", features = [ "full" ] }
+tokio-stream = { version = "0.1.16", features = [ "fs" ] }
+tokio-util = { version = "0.7", features = [ "compat" ] }
+zip = { version = "2.2.0" }
# crypto
digest = { version = "0.10" }
-sha1 = { version = "0.10" }
-sha2 = { version = "0.10" }
md-5 = { version = "0.10" }
murmur2 = { version = "0.1.0" }
+sha1 = { version = "0.10" }
+sha2 = { version = "0.10" }
# db
sea-orm = { version = "1.1", features = [
@@ -134,85 +134,91 @@ sea-orm = { version = "1.1", features = [
"macros",
"with-chrono",
"with-json",
- "with-uuid"
+ "with-uuid",
+] }
+sea-orm-migration = { version = "1.1", features = [
+ "sqlx-sqlite",
+ "runtime-tokio-rustls",
+ "with-chrono",
+ "with-json",
+ "with-uuid",
] }
-sea-orm-migration = { version = "1.1", features = [ "sqlx-sqlite", "runtime-tokio-rustls" ] }
# utils
-thiserror = { version = "1.0" }
anyhow = { version = "1.0" }
-sysinfo = { version = "0.31", features = [ "serde" ] }
+async-trait = { version = "0.1" }
+cc = { version = "1.1" }
discord-rich-presence = { version = "0.2" }
-indicatif = { version = "0.17" }
futures = { version = "0.3" }
futures-lite = { version = "2.6" }
-async-trait = { version = "0.1" }
-whoami = { version = "1.5" }
-cc = { version = "1.1" }
+indicatif = { version = "0.17" }
libc = { version = "0.2" }
merge = { version = "0.2" }
+sysinfo = { version = "0.31", features = [ "serde" ] }
+thiserror = { version = "1.0" }
+whoami = { version = "1.5" }
# macro
-syn = { version = "2" }
-quote = { version = "1" }
paste = { version = "1" }
+quote = { version = "1" }
+syn = { version = "2" }
[workspace.lints.clippy]
all = { level = "warn", priority = -1 }
-pedantic = { level = "warn", priority = -1 }
-correctness = { level = "warn", priority = -1 }
-perf = { level = "warn", priority = -1 }
-style = { level = "warn", priority = -1 }
-suspicious = { level = "warn", priority = -1 }
-complexity = { level = "warn", priority = -1 }
-nursery = { level = "warn", priority = -1 }
-unnecessary_cast = "warn"
cast_lossless = "warn"
+cast_precision_loss = "allow"
+complexity = { level = "warn", priority = -1 }
+correctness = { level = "warn", priority = -1 }
dbg_macro = "warn"
deprecated_cfg_attr = "warn"
-separated_literal_suffix = "warn"
-missing_errors_doc = "allow"
future_not_send = "allow"
-module_name_repetitions = "allow"
-struct_field_names = "allow"
-cast_precision_loss = "allow"
+missing_errors_doc = "allow"
missing_panics_doc = "allow"
-significant_drop_tightening = "allow" # tmp
+module_name_repetitions = "allow"
needless_return = "allow"
+nursery = { level = "warn", priority = -1 }
+pedantic = { level = "warn", priority = -1 }
+perf = { level = "warn", priority = -1 }
+separated_literal_suffix = "warn"
+significant_drop_tightening = "allow" # tmp
+struct_field_names = "allow"
+style = { level = "warn", priority = -1 }
+suspicious = { level = "warn", priority = -1 }
+unnecessary_cast = "warn"
[workspace.lints.rust]
-unused_qualifications = "warn"
+deprecated = "warn"
+deprecated_in_future = "forbid"
rust_2018_idioms = { level = "warn", priority = -1 }
trivial_casts = "warn"
trivial_numeric_casts = "warn"
unused_allocation = "warn"
unused_import_braces = "warn"
-deprecated = "warn"
-deprecated_in_future = "forbid"
unused_must_use = "deny"
+unused_qualifications = "warn"
[profile.dev]
# add debug symbols/logic and optimize for reduced build time (parallel)
-debug = true # add debug info
-opt-level = 0 # reduce optimizations
-strip = "none" # don't remove debug symbols
-incremental = true # enable incremental compilation
-split-debuginfo = "unpacked" # unpack debug info
-lto = false # disable link time optimizations, reduce compile time
codegen-units = 256 # make parallel incremental builds faster
-rpath = false # disable rpath
+debug = true # add debug info
+incremental = true # enable incremental compilation
+lto = false # disable link time optimizations, reduce compile time
+opt-level = 0 # reduce optimizations
panic = "unwind" # add unwind panic logic because some deps require it
+rpath = false # disable rpath
+split-debuginfo = "unpacked" # unpack debug info
+strip = "none" # don't remove debug symbols
# add debug symbols/logic and optimize for reduced build time (parallel)
[profile.dev-debug]
-inherits = "dev" # extend the dev profile
-split-debuginfo = "none" # don't split debug info
-opt-level = 0 # reduce optimizations
-debug = "full" # add full debug info
-strip = "none" # don't remove debug symbols
-lto = "off" # disable link time optimizations, reduce compile time
codegen-units = 256 # make parallel incremental builds faster
+debug = "full" # add full debug info
incremental = true # enable incremental compilation
+inherits = "dev" # extend the dev profile
+lto = "off" # disable link time optimizations, reduce compile time
+opt-level = 0 # reduce optimizations
+split-debuginfo = "none" # don't split debug info
+strip = "none" # don't remove debug symbols
# set the settings for build scripts and proc-macros.
[profile.dev.build-override]
@@ -220,20 +226,20 @@ opt-level = 3 # optimize build
# set the default for dependencies, except workspace members.
[profile.dev.package."*"]
-opt-level = 3 # optimize build
incremental = false # disable incremental compilation
+opt-level = 3 # optimize build
# set the default for dependencies, except workspace members.
[profile.dev-debug.package."*"]
-inherits = "dev" # extend the dev profile
-opt-level = 3 # optimize build
debug = "full" # add full debug info
incremental = false # disable incremental compilation
+inherits = "dev" # extend the dev profile
+opt-level = 3 # optimize build
# optimize for speed and reduce size for release
[profile.release]
-panic = "abort" # strip panic cleanup logic
codegen-units = 1 # compile crate one after another for better optimization
lto = true # enable link time optimization
opt-level = "s" # optimize binary
+panic = "abort" # strip panic cleanup logic
strip = true # remove debug symbols
diff --git a/packages/core/examples/launch.rs b/packages/core/examples/launch.rs
index d3d23f82..7614e7ac 100644
--- a/packages/core/examples/launch.rs
+++ b/packages/core/examples/launch.rs
@@ -53,7 +53,7 @@ pub async fn main() -> LauncherResult<()> {
let cluster_name = "Launchable";
let path = dirs.clusters_dir().join(cluster_name);
- let mut cluster = if let Some(cluster) = api::cluster::dao::get_cluster_by_path(&path).await? {
+ let mut cluster = if let Some(cluster) = api::cluster::dao::get_cluster_by_folder_name(&path).await? {
cluster
} else {
api::cluster::create_cluster(cluster_name, "1.20.4", GameLoader::Fabric, None, None).await?
@@ -66,7 +66,7 @@ pub async fn main() -> LauncherResult<()> {
Ok(cluster)
}).await?;
- println!("Using cluster: {} at '{}'", cluster.name, cluster.path);
+ println!("Using cluster: {} at '{}'", cluster.name, cluster.folder_name);
// Launch the game
let process = api::game::launch::launch_minecraft(&mut cluster, creds, None).await?;
diff --git a/packages/core/examples/packages.rs b/packages/core/examples/packages.rs
new file mode 100644
index 00000000..68b8eaf3
--- /dev/null
+++ b/packages/core/examples/packages.rs
@@ -0,0 +1,39 @@
+use onelauncher_core::{api::{cluster, packages::{self, download_package, provider::ProviderExt}, proxy::ProxyDynamic}, error::LauncherResult, initialize_core, store::{CoreOptions, Dirs}};
+use onelauncher_entity::{loader::GameLoader, package::Provider};
+
+#[tokio::main]
+pub async fn main() -> LauncherResult<()> {
+ initialize_core(CoreOptions::default(), ProxyDynamic::default()).await?;
+
+ let dirs = Dirs::get().await?;
+
+ // Here we create the cluster
+ let cluster_name = "Chatting Profile";
+ let path = dirs.clusters_dir().join(cluster_name);
+
+ let cluster = if let Some(cluster) = cluster::dao::get_cluster_by_folder_name(&path).await? {
+ cluster
+ } else {
+ cluster::create_cluster(cluster_name, "1.8.9", GameLoader::Forge, None, None).await?
+ };
+
+ // Fetch our package and its versions
+ let provider = Provider::Modrinth;
+ let package = &provider.get("chatting").await?;
+ let versions = provider.get_versions(package.version_ids.as_slice()).await?;
+
+ let version = versions.iter().find(|v| v.mc_versions.contains(&"1.8.9".to_string())).expect("Version supporting 1.8.9 not found");
+
+ // We can now download the package
+ let model = download_package(package, version, None).await?;
+
+ // Link the package to the cluster
+ packages::link_package(&model, &cluster, None).await?;
+
+ // Check if the package is hard linked
+ let cluster_dir = dirs.clusters_dir().join(cluster.folder_name.clone());
+ let pkg_path = cluster_dir.join(model.package_type.folder_name()).join(model.file_name.as_str());
+ assert!(tokio::fs::metadata(&pkg_path).await.is_ok(), "Package not found in cluster directory");
+
+ Ok(())
+}
\ No newline at end of file
diff --git a/packages/core/src/api/cluster/dao.rs b/packages/core/src/api/cluster/dao.rs
index af494462..ad2d7bf7 100644
--- a/packages/core/src/api/cluster/dao.rs
+++ b/packages/core/src/api/cluster/dao.rs
@@ -10,7 +10,7 @@ pub type ClusterId = i64;
/// Inserts a cluster in the database.
pub async fn insert_cluster(
name: &str,
- path: &str,
+ folder_name: &str,
mc_version: &str,
mc_loader: GameLoader,
mc_loader_version: Option<&str>,
@@ -21,7 +21,7 @@ pub async fn insert_cluster(
let model = clusters::ActiveModel {
name: Set(name.to_string()),
- path: Set(path.to_string()),
+ folder_name: Set(folder_name.to_string()),
mc_version: Set(mc_version.to_string()),
mc_loader: Set(mc_loader),
mc_loader_version: Set(mc_loader_version.map(String::from)),
@@ -96,12 +96,12 @@ pub async fn get_cluster_by_id(id: ClusterId) -> LauncherResult