diff options
| author | 2026-01-13 17:54:42 +0800 | |
|---|---|---|
| committer | 2026-01-13 17:54:42 +0800 | |
| commit | 50f4725c657c6583452d9c85f9b934c3cf2a0f70 (patch) | |
| tree | 8d8de216b134a04507d68dd2dc7516b8dbe433bd /src-tauri | |
| parent | f7cabe5a0388518be50eb225500b7b4800109274 (diff) | |
| download | DropOut-50f4725c657c6583452d9c85f9b934c3cf2a0f70.tar.gz DropOut-50f4725c657c6583452d9c85f9b934c3cf2a0f70.zip | |
chore(style): format code
Diffstat (limited to 'src-tauri')
| -rw-r--r-- | src-tauri/build.rs | 2 | ||||
| -rw-r--r-- | src-tauri/src/core/config.rs | 2 | ||||
| -rw-r--r-- | src-tauri/src/core/downloader.rs | 99 | ||||
| -rw-r--r-- | src-tauri/src/core/game_version.rs | 4 | ||||
| -rw-r--r-- | src-tauri/src/core/mod.rs | 4 | ||||
| -rw-r--r-- | src-tauri/src/core/rules.rs | 14 | ||||
| -rw-r--r-- | src-tauri/src/launcher/config.rs | 2 | ||||
| -rw-r--r-- | src-tauri/src/launcher/launcher.rs | 7 | ||||
| -rw-r--r-- | src-tauri/src/launcher/mod.rs | 2 | ||||
| -rw-r--r-- | src-tauri/src/main.rs | 360 | ||||
| -rw-r--r-- | src-tauri/src/utils/mod.rs | 2 | ||||
| -rw-r--r-- | src-tauri/src/utils/zip.rs | 20 |
12 files changed, 301 insertions, 217 deletions
diff --git a/src-tauri/build.rs b/src-tauri/build.rs index 82d481c..d860e1e 100644 --- a/src-tauri/build.rs +++ b/src-tauri/build.rs @@ -1,3 +1,3 @@ fn main() { tauri_build::build() -}
\ No newline at end of file +} diff --git a/src-tauri/src/core/config.rs b/src-tauri/src/core/config.rs index 5558786..47c5306 100644 --- a/src-tauri/src/core/config.rs +++ b/src-tauri/src/core/config.rs @@ -34,7 +34,7 @@ impl ConfigState { pub fn new(app_handle: &AppHandle) -> Self { let app_dir = app_handle.path().app_data_dir().unwrap(); let config_path = app_dir.join("config.json"); - + let config = if config_path.exists() { let content = fs::read_to_string(&config_path).unwrap_or_default(); serde_json::from_str(&content).unwrap_or_default() diff --git a/src-tauri/src/core/downloader.rs b/src-tauri/src/core/downloader.rs index 33404e9..5f6ec80 100644 --- a/src-tauri/src/core/downloader.rs +++ b/src-tauri/src/core/downloader.rs @@ -1,9 +1,9 @@ +use futures::StreamExt; +use serde::{Deserialize, Serialize}; use std::path::PathBuf; -use serde::{Serialize, Deserialize}; +use std::sync::Arc; use tauri::{Emitter, Window}; -use futures::StreamExt; use tokio::io::AsyncWriteExt; -use std::sync::Arc; use tokio::sync::Semaphore; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -24,7 +24,7 @@ pub struct ProgressEvent { pub async fn download_files(window: Window, tasks: Vec<DownloadTask>) -> Result<(), String> { let client = reqwest::Client::new(); let semaphore = Arc::new(Semaphore::new(10)); // Max 10 concurrent downloads - + // Notify start (total files) let _ = window.emit("download-start", tasks.len()); @@ -32,43 +32,49 @@ pub async fn download_files(window: Window, tasks: Vec<DownloadTask>) -> Result< let client = client.clone(); let window = window.clone(); let semaphore = semaphore.clone(); - + async move { let _permit = semaphore.acquire().await.unwrap(); let file_name = task.path.file_name().unwrap().to_string_lossy().to_string(); // 1. Check if file exists and verify SHA1 if task.path.exists() { - let _ = window.emit("download-progress", ProgressEvent { - file: file_name.clone(), - downloaded: 0, - total: 0, - status: "Verifying".into(), - }); + let _ = window.emit( + "download-progress", + ProgressEvent { + file: file_name.clone(), + downloaded: 0, + total: 0, + status: "Verifying".into(), + }, + ); if let Some(expected_sha1) = &task.sha1 { if let Ok(data) = tokio::fs::read(&task.path).await { - let mut hasher = sha1::Sha1::new(); - use sha1::Digest; - hasher.update(&data); - let result = hex::encode(hasher.finalize()); - if &result == expected_sha1 { - // Already valid - let _ = window.emit("download-progress", ProgressEvent { - file: file_name.clone(), - downloaded: 0, - total: 0, - status: "Skipped".into(), - }); - return Ok(()); - } + let mut hasher = sha1::Sha1::new(); + use sha1::Digest; + hasher.update(&data); + let result = hex::encode(hasher.finalize()); + if &result == expected_sha1 { + // Already valid + let _ = window.emit( + "download-progress", + ProgressEvent { + file: file_name.clone(), + downloaded: 0, + total: 0, + status: "Skipped".into(), + }, + ); + return Ok(()); + } } } } // 2. Download if let Some(parent) = task.path.parent() { - let _ = tokio::fs::create_dir_all(parent).await; + let _ = tokio::fs::create_dir_all(parent).await; } match client.get(&task.url).send().await { @@ -78,7 +84,7 @@ pub async fn download_files(window: Window, tasks: Vec<DownloadTask>) -> Result< Ok(f) => f, Err(e) => return Err(format!("Create file error: {}", e)), }; - + let mut downloaded: u64 = 0; loop { match resp.chunk().await { @@ -87,35 +93,44 @@ pub async fn download_files(window: Window, tasks: Vec<DownloadTask>) -> Result< return Err(format!("Write error: {}", e)); } downloaded += chunk.len() as u64; - let _ = window.emit("download-progress", ProgressEvent { - file: file_name.clone(), - downloaded, - total: total_size, - status: "Downloading".into(), - }); + let _ = window.emit( + "download-progress", + ProgressEvent { + file: file_name.clone(), + downloaded, + total: total_size, + status: "Downloading".into(), + }, + ); } Ok(None) => break, Err(e) => return Err(format!("Download error: {}", e)), } } - }, + } Err(e) => return Err(format!("Request error: {}", e)), } - let _ = window.emit("download-progress", ProgressEvent { - file: file_name.clone(), - downloaded: 0, - total: 0, - status: "Finished".into(), - }); + let _ = window.emit( + "download-progress", + ProgressEvent { + file: file_name.clone(), + downloaded: 0, + total: 0, + status: "Finished".into(), + }, + ); Ok(()) } }); // Buffer unordered to run concurrently - tasks_stream.buffer_unordered(10).collect::<Vec<Result<(), String>>>().await; - + tasks_stream + .buffer_unordered(10) + .collect::<Vec<Result<(), String>>>() + .await; + let _ = window.emit("download-complete", ()); Ok(()) } diff --git a/src-tauri/src/core/game_version.rs b/src-tauri/src/core/game_version.rs index 9eb8d67..572882f 100644 --- a/src-tauri/src/core/game_version.rs +++ b/src-tauri/src/core/game_version.rs @@ -56,9 +56,9 @@ pub struct Rule { #[derive(Debug, Deserialize)] pub struct OsRule { - pub name: Option<String>, // "linux", "osx", "windows" + pub name: Option<String>, // "linux", "osx", "windows" pub version: Option<String>, // Regex - pub arch: Option<String>, // "x86" + pub arch: Option<String>, // "x86" } #[derive(Debug, Deserialize)] diff --git a/src-tauri/src/core/mod.rs b/src-tauri/src/core/mod.rs index 6f7c3fe..746afe6 100644 --- a/src-tauri/src/core/mod.rs +++ b/src-tauri/src/core/mod.rs @@ -1,6 +1,6 @@ -pub mod manifest; pub mod auth; +pub mod config; pub mod downloader; pub mod game_version; +pub mod manifest; pub mod rules; -pub mod config; diff --git a/src-tauri/src/core/rules.rs b/src-tauri/src/core/rules.rs index 214d9bc..877982a 100644 --- a/src-tauri/src/core/rules.rs +++ b/src-tauri/src/core/rules.rs @@ -11,7 +11,7 @@ pub fn is_library_allowed(rules: &Option<Vec<Rule>>) -> bool { return true; } - // Default depends on the first rule theoretically, but usually "allow" if no "disallow" matches? + // Default depends on the first rule theoretically, but usually "allow" if no "disallow" matches? // Actually MC logic: implicit disallow? No, implicit allow usually? // Official launcher Rule logic: // "Libraries are allowed unless restricted by a rule." @@ -24,23 +24,23 @@ pub fn is_library_allowed(rules: &Option<Vec<Rule>>) -> bool { // This implies base allowed, but OS X disallowed. // Pattern 2: [ {action: allow, os: "osx"} ] // This implies ONLY osx allowed? - + // Correct logic: // If rules are present, start with result = false (deny all). // Loop through rules. If a rule applies (os matches), update result to (action == "allow"). // Wait, let's verify. // If the list is [ {action: allow} ], result becomes true. - // If list is [ {action: allow}, {action: disallow, os: "osx"} ]. + // If list is [ {action: allow}, {action: disallow, os: "osx"} ]. // On Linux: Rule 1 matches -> true. Rule 2 (osx) doesn't match -> ignore. Final: true. // On OSX: Rule 1 matches -> true. Rule 2 matches -> false. Final: false. - + // So: Start false. Apply rules in order. - + let mut allowed = false; for rule in rules { if rule_matches(rule) { - allowed = (rule.action == "allow"); + allowed = rule.action == "allow"; } } allowed @@ -60,7 +60,7 @@ fn rule_matches(rule: &Rule) -> bool { } else { // OS rule exists but name is None? Maybe checking version/arch only. // For simplicity, mostly name is used. - true + true } } } diff --git a/src-tauri/src/launcher/config.rs b/src-tauri/src/launcher/config.rs index fafc229..7637268 100644 --- a/src-tauri/src/launcher/config.rs +++ b/src-tauri/src/launcher/config.rs @@ -10,4 +10,4 @@ impl Config { resolution, } } -}
\ No newline at end of file +} diff --git a/src-tauri/src/launcher/launcher.rs b/src-tauri/src/launcher/launcher.rs index 5ccf443..22a8881 100644 --- a/src-tauri/src/launcher/launcher.rs +++ b/src-tauri/src/launcher/launcher.rs @@ -10,6 +10,9 @@ impl Launcher { pub fn launch(&self) { // 启动游戏的逻辑 println!("启动游戏,用户名: {}", self.config.username); - println!("分辨率: {}x{}", self.config.resolution.0, self.config.resolution.1); + println!( + "分辨率: {}x{}", + self.config.resolution.0, self.config.resolution.1 + ); } -}
\ No newline at end of file +} diff --git a/src-tauri/src/launcher/mod.rs b/src-tauri/src/launcher/mod.rs index 0359d3e..100de01 100644 --- a/src-tauri/src/launcher/mod.rs +++ b/src-tauri/src/launcher/mod.rs @@ -16,4 +16,4 @@ pub fn start() { // 启动游戏 launcher.launch(); -}
\ No newline at end of file +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 91b61ed..02a2b18 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,9 +1,9 @@ // Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] -use tauri::{Manager, State, Window, Emitter}; // Added Emitter use std::process::Stdio; -use tokio::io::{BufReader, AsyncBufReadExt}; +use tauri::{Emitter, Manager, State, Window}; // Added Emitter +use tokio::io::{AsyncBufReadExt, BufReader}; use tokio::process::Command; mod core; @@ -15,40 +15,56 @@ async fn start_game( window: Window, auth_state: State<'_, core::auth::AccountState>, config_state: State<'_, core::config::ConfigState>, - version_id: String + version_id: String, ) -> Result<String, String> { println!("Backend received StartGame for {}", version_id); - + // Check for active account - let account = auth_state.active_account.lock().unwrap().clone() + let account = auth_state + .active_account + .lock() + .unwrap() + .clone() .ok_or("No active account found. Please login first.")?; - + let config = config_state.config.lock().unwrap().clone(); // Get App Data Directory (e.g., ~/.local/share/com.dropout.launcher or similar) // The identifier is set in tauri.conf.json. // If not accessible, use a specific logic. let app_handle = window.app_handle(); - let game_dir = app_handle.path().app_data_dir() + let game_dir = app_handle + .path() + .app_data_dir() .map_err(|e| format!("Failed to get app data dir: {}", e))?; - + // Ensure game directory exists - tokio::fs::create_dir_all(&game_dir).await.map_err(|e| e.to_string())?; + tokio::fs::create_dir_all(&game_dir) + .await + .map_err(|e| e.to_string())?; println!("Game Directory: {:?}", game_dir); // 1. Fetch manifest to find the version URL - let manifest = core::manifest::fetch_version_manifest().await.map_err(|e| e.to_string())?; - + let manifest = core::manifest::fetch_version_manifest() + .await + .map_err(|e| e.to_string())?; + // Find the version info - let version_info = manifest.versions.iter().find(|v| v.id == version_id) + let version_info = manifest + .versions + .iter() + .find(|v| v.id == version_id) .ok_or_else(|| format!("Version {} not found in manifest", version_id))?; - + // 2. Fetch specific version JSON (client.jar info) let version_url = &version_info.url; let version_details: core::game_version::GameVersion = reqwest::get(version_url) - .await.map_err(|e| e.to_string())? - .json().await.map_err(|e| e.to_string())?; + .await + .map_err(|e| e.to_string())? + .json() + .await + .map_err(|e| e.to_string())?; // 3. Prepare download tasks let mut download_tasks = Vec::new(); @@ -69,59 +85,64 @@ async fn start_game( println!("Processing libraries..."); let libraries_dir = game_dir.join("libraries"); let mut native_libs_paths = Vec::new(); // Store paths to native jars for extraction - + for lib in &version_details.libraries { if core::rules::is_library_allowed(&lib.rules) { - // 1. Standard Library - if let Some(downloads) = &lib.downloads { - if let Some(artifact) = &downloads.artifact { - let path_str = artifact.path.clone().unwrap_or_else(|| { - format!("{}.jar", lib.name) - }); - - let mut lib_path = libraries_dir.clone(); - lib_path.push(path_str); - - download_tasks.push(core::downloader::DownloadTask { - url: artifact.url.clone(), - path: lib_path, - sha1: Some(artifact.sha1.clone()), - }); - } - - // 2. Native Library (classifiers) - // e.g. "natives-linux": { ... } - if let Some(classifiers) = &downloads.classifiers { - // Determine the key based on OS - // Linux usually "natives-linux", Windows "natives-windows", Mac "natives-osx" (or macos) - let os_key = if cfg!(target_os = "linux") { - "natives-linux" - } else if cfg!(target_os = "windows") { - "natives-windows" - } else if cfg!(target_os = "macos") { - "natives-osx" // or natives-macos? check json - } else { - "" - }; - - if let Some(native_artifact_value) = classifiers.get(os_key) { - // Parse it as DownloadArtifact - if let Ok(native_artifact) = serde_json::from_value::<core::game_version::DownloadArtifact>(native_artifact_value.clone()) { - let path_str = native_artifact.path.clone().unwrap(); // Natives usually have path - let mut native_path = libraries_dir.clone(); - native_path.push(&path_str); - - download_tasks.push(core::downloader::DownloadTask { - url: native_artifact.url, - path: native_path.clone(), - sha1: Some(native_artifact.sha1), - }); - - native_libs_paths.push(native_path); - } - } - } - } + // 1. Standard Library + if let Some(downloads) = &lib.downloads { + if let Some(artifact) = &downloads.artifact { + let path_str = artifact + .path + .clone() + .unwrap_or_else(|| format!("{}.jar", lib.name)); + + let mut lib_path = libraries_dir.clone(); + lib_path.push(path_str); + + download_tasks.push(core::downloader::DownloadTask { + url: artifact.url.clone(), + path: lib_path, + sha1: Some(artifact.sha1.clone()), + }); + } + + // 2. Native Library (classifiers) + // e.g. "natives-linux": { ... } + if let Some(classifiers) = &downloads.classifiers { + // Determine the key based on OS + // Linux usually "natives-linux", Windows "natives-windows", Mac "natives-osx" (or macos) + let os_key = if cfg!(target_os = "linux") { + "natives-linux" + } else if cfg!(target_os = "windows") { + "natives-windows" + } else if cfg!(target_os = "macos") { + "natives-osx" // or natives-macos? check json + } else { + "" + }; + + if let Some(native_artifact_value) = classifiers.get(os_key) { + // Parse it as DownloadArtifact + if let Ok(native_artifact) = + serde_json::from_value::<core::game_version::DownloadArtifact>( + native_artifact_value.clone(), + ) + { + let path_str = native_artifact.path.clone().unwrap(); // Natives usually have path + let mut native_path = libraries_dir.clone(); + native_path.push(&path_str); + + download_tasks.push(core::downloader::DownloadTask { + url: native_artifact.url, + path: native_path.clone(), + sha1: Some(native_artifact.sha1), + }); + + native_libs_paths.push(native_path); + } + } + } + } } } @@ -133,23 +154,35 @@ async fn start_game( // Download Asset Index JSON let asset_index_path = indexes_dir.join(format!("{}.json", version_details.asset_index.id)); - + // Check if index exists or download it - // Note: We need the content of this file to parse it. - // If we just add it to download_tasks, we can't parse it *now*. + // Note: We need the content of this file to parse it. + // If we just add it to download_tasks, we can't parse it *now*. // So we must download it immediately (await) before processing objects. let asset_index_content: String = if asset_index_path.exists() { - tokio::fs::read_to_string(&asset_index_path).await.map_err(|e| e.to_string())? + tokio::fs::read_to_string(&asset_index_path) + .await + .map_err(|e| e.to_string())? } else { - println!("Downloading asset index from {}", version_details.asset_index.url); + println!( + "Downloading asset index from {}", + version_details.asset_index.url + ); let content = reqwest::get(&version_details.asset_index.url) - .await.map_err(|e| e.to_string())? - .text().await.map_err(|e| e.to_string())?; - + .await + .map_err(|e| e.to_string())? + .text() + .await + .map_err(|e| e.to_string())?; + // Save it for next time - tokio::fs::create_dir_all(&indexes_dir).await.map_err(|e| e.to_string())?; - tokio::fs::write(&asset_index_path, &content).await.map_err(|e| e.to_string())?; + tokio::fs::create_dir_all(&indexes_dir) + .await + .map_err(|e| e.to_string())?; + tokio::fs::write(&asset_index_path, &content) + .await + .map_err(|e| e.to_string())?; content }; @@ -158,22 +191,26 @@ async fn start_game( hash: String, size: u64, } - + #[derive(serde::Deserialize, Debug)] struct AssetIndexJson { objects: std::collections::HashMap<String, AssetObject>, } - let asset_index_parsed: AssetIndexJson = serde_json::from_str(&asset_index_content).map_err(|e| e.to_string())?; - + let asset_index_parsed: AssetIndexJson = + serde_json::from_str(&asset_index_content).map_err(|e| e.to_string())?; + println!("Processing {} assets...", asset_index_parsed.objects.len()); - + for (_name, object) in asset_index_parsed.objects { let hash = object.hash; let prefix = &hash[0..2]; let path = objects_dir.join(prefix).join(&hash); - let url = format!("https://resources.download.minecraft.net/{}/{}", prefix, hash); - + let url = format!( + "https://resources.download.minecraft.net/{}/{}", + prefix, hash + ); + download_tasks.push(core::downloader::DownloadTask { url, path, @@ -181,61 +218,74 @@ async fn start_game( }); } + println!( + "Total download tasks (Client + Libs + Assets): {}", + download_tasks.len() + ); - println!("Total download tasks (Client + Libs + Assets): {}", download_tasks.len()); - // 4. Start Download - core::downloader::download_files(window.clone(), download_tasks).await.map_err(|e| e.to_string())?; + core::downloader::download_files(window.clone(), download_tasks) + .await + .map_err(|e| e.to_string())?; // 5. Extract Natives println!("Extracting natives..."); let natives_dir = game_dir.join("versions").join(&version_id).join("natives"); - + // Clean old natives if they exist to prevent conflicts if natives_dir.exists() { - tokio::fs::remove_dir_all(&natives_dir).await.map_err(|e| e.to_string())?; + tokio::fs::remove_dir_all(&natives_dir) + .await + .map_err(|e| e.to_string())?; } - tokio::fs::create_dir_all(&natives_dir).await.map_err(|e| e.to_string())?; + tokio::fs::create_dir_all(&natives_dir) + .await + .map_err(|e| e.to_string())?; for path in native_libs_paths { if path.exists() { - println!("Extracting native: {:?}", path); - utils::zip::extract_zip(&path, &natives_dir)?; + println!("Extracting native: {:?}", path); + utils::zip::extract_zip(&path, &natives_dir)?; } } // 6. Construct Classpath - let cp_separator = if cfg!(target_os = "windows") { ";" } else { ":" }; + let cp_separator = if cfg!(target_os = "windows") { + ";" + } else { + ":" + }; let mut classpath_entries = Vec::new(); - + // Add libraries for lib in &version_details.libraries { if core::rules::is_library_allowed(&lib.rules) { - if let Some(downloads) = &lib.downloads { - if let Some(artifact) = &downloads.artifact { - let path_str = artifact.path.clone().unwrap_or_else(|| { - format!("{}.jar", lib.name) - }); - let lib_path = libraries_dir.join(path_str); - classpath_entries.push(lib_path.to_string_lossy().to_string()); - } - } + if let Some(downloads) = &lib.downloads { + if let Some(artifact) = &downloads.artifact { + let path_str = artifact + .path + .clone() + .unwrap_or_else(|| format!("{}.jar", lib.name)); + let lib_path = libraries_dir.join(path_str); + classpath_entries.push(lib_path.to_string_lossy().to_string()); + } + } } } // Add client jar classpath_entries.push(client_path.to_string_lossy().to_string()); - + let classpath = classpath_entries.join(cp_separator); // 7. Prepare Arguments let mut args = Vec::new(); let natives_path = natives_dir.to_string_lossy().to_string(); - + // 7a. JVM Arguments (Simplified for now) // We inject standard convenient defaults. // TODO: Parse 'arguments.jvm' from version.json for full compatibility (Mac M1 support etc) args.push(format!("-Djava.library.path={}", natives_path)); - args.push(format!("-Xmx{}M", config.max_memory)); + args.push(format!("-Xmx{}M", config.max_memory)); args.push(format!("-Xms{}M", config.min_memory)); args.push("-cp".to_string()); args.push(classpath); @@ -250,7 +300,10 @@ async fn start_game( replacements.insert("${version_name}", version_id.clone()); replacements.insert("${game_directory}", game_dir.to_string_lossy().to_string()); replacements.insert("${assets_root}", assets_dir.to_string_lossy().to_string()); - replacements.insert("${assets_index_name}", version_details.asset_index.id.clone()); + replacements.insert( + "${assets_index_name}", + version_details.asset_index.id.clone(), + ); replacements.insert("${auth_uuid}", account.uuid.clone()); replacements.insert("${auth_access_token}", "null".to_string()); // Offline replacements.insert("${user_type}", "mojang".to_string()); @@ -269,31 +322,33 @@ async fn start_game( } else if let Some(args_obj) = &version_details.arguments { if let Some(game_args) = &args_obj.game { // Can be array of strings or objects - if let Some(list) = game_args.as_array() { - for item in list { - if let Some(s) = item.as_str() { - let mut arg = s.to_string(); - for (key, val) in &replacements { - arg = arg.replace(key, val); - } - args.push(arg); - } else if let Some(obj) = item.as_object() { - // Check rules - // Simplified: if it has "value", and rules pass. - // For now, assuming rules pass if no "rules" field or simplistic check - // Ideally we should implement a helper to check rules for args just like libs - - let allow = if let Some(rules_val) = obj.get("rules") { - if let Ok(rules) = serde_json::from_value::<Vec<core::game_version::Rule>>(rules_val.clone()) { - core::rules::is_library_allowed(&Some(rules)) - } else { - true // Parse error, assume allow? or disallow. - } - } else { - true - }; - - if allow { + if let Some(list) = game_args.as_array() { + for item in list { + if let Some(s) = item.as_str() { + let mut arg = s.to_string(); + for (key, val) in &replacements { + arg = arg.replace(key, val); + } + args.push(arg); + } else if let Some(obj) = item.as_object() { + // Check rules + // Simplified: if it has "value", and rules pass. + // For now, assuming rules pass if no "rules" field or simplistic check + // Ideally we should implement a helper to check rules for args just like libs + + let allow = if let Some(rules_val) = obj.get("rules") { + if let Ok(rules) = serde_json::from_value::<Vec<core::game_version::Rule>>( + rules_val.clone(), + ) { + core::rules::is_library_allowed(&Some(rules)) + } else { + true // Parse error, assume allow? or disallow. + } + } else { + true + }; + + if allow { if let Some(val) = obj.get("value") { if let Some(s) = val.as_str() { let mut arg = s.to_string(); @@ -313,17 +368,17 @@ async fn start_game( } } } - } - } - } - } + } + } + } + } } } println!("Launching game with {} args...", args.len()); // Debug: Print arguments to help diagnose issues println!("Launch Args: {:?}", args); - + // Spawn the process let mut command = Command::new(&config.java_path); command.args(&args); @@ -332,16 +387,24 @@ async fn start_game( command.stderr(Stdio::piped()); // Spawn and handle output - let mut child = command.spawn().map_err(|e| format!("Failed to launch java: {}", e))?; - - let stdout = child.stdout.take().expect("child did not have a handle to stdout"); - let stderr = child.stderr.take().expect("child did not have a handle to stderr"); + let mut child = command + .spawn() + .map_err(|e| format!("Failed to launch java: {}", e))?; + + let stdout = child + .stdout + .take() + .expect("child did not have a handle to stdout"); + let stderr = child + .stderr + .take() + .expect("child did not have a handle to stderr"); let window_rx = window.clone(); tokio::spawn(async move { let mut reader = BufReader::new(stdout).lines(); while let Ok(Some(line)) = reader.next_line().await { - let _ = window_rx.emit("game-stdout", line); + let _ = window_rx.emit("game-stdout", line); } }); @@ -349,7 +412,7 @@ async fn start_game( tokio::spawn(async move { let mut reader = BufReader::new(stderr).lines(); while let Ok(Some(line)) = reader.next_line().await { - let _ = window_rx_err.emit("game-stderr", line); + let _ = window_rx_err.emit("game-stderr", line); } }); @@ -370,11 +433,8 @@ async fn login_offline( username: String, ) -> Result<core::auth::OfflineAccount, String> { let uuid = core::auth::generate_offline_uuid(&username); - let account = core::auth::OfflineAccount { - username, - uuid, - }; - + let account = core::auth::OfflineAccount { username, uuid }; + *state.active_account.lock().unwrap() = Some(account.clone()); Ok(account) } @@ -419,10 +479,10 @@ fn main() { Ok(()) }) .invoke_handler(tauri::generate_handler![ - start_game, - get_versions, - login_offline, - get_active_account, + start_game, + get_versions, + login_offline, + get_active_account, logout, get_settings, save_settings diff --git a/src-tauri/src/utils/mod.rs b/src-tauri/src/utils/mod.rs index 265beda..41691c2 100644 --- a/src-tauri/src/utils/mod.rs +++ b/src-tauri/src/utils/mod.rs @@ -30,4 +30,4 @@ pub mod config_parser { } config } -}
\ No newline at end of file +} diff --git a/src-tauri/src/utils/zip.rs b/src-tauri/src/utils/zip.rs index 3ddf0f2..a03c975 100644 --- a/src-tauri/src/utils/zip.rs +++ b/src-tauri/src/utils/zip.rs @@ -1,12 +1,16 @@ -use std::path::Path; use std::fs; +use std::path::Path; pub fn extract_zip(zip_path: &Path, extract_to: &Path) -> Result<(), String> { - let file = fs::File::open(zip_path).map_err(|e| format!("Failed to open zip {}: {}", zip_path.display(), e))?; - let mut archive = zip::ZipArchive::new(file).map_err(|e| format!("Failed to read zip: {}", e))?; + let file = fs::File::open(zip_path) + .map_err(|e| format!("Failed to open zip {}: {}", zip_path.display(), e))?; + let mut archive = + zip::ZipArchive::new(file).map_err(|e| format!("Failed to read zip: {}", e))?; for i in 0..archive.len() { - let mut file = archive.by_index(i).map_err(|e| format!("Failed to read zip entry: {}", e))?; + let mut file = archive + .by_index(i) + .map_err(|e| format!("Failed to read zip entry: {}", e))?; let outpath = match file.enclosed_name() { Some(path) => extract_to.join(path), None => continue, @@ -22,11 +26,13 @@ pub fn extract_zip(zip_path: &Path, extract_to: &Path) -> Result<(), String> { } else { if let Some(p) = outpath.parent() { if !p.exists() { - fs::create_dir_all(&p).map_err(|e| format!("Failed to create dir: {}", e))?; + fs::create_dir_all(p).map_err(|e| format!("Failed to create dir: {}", e))?; } } - let mut outfile = fs::File::create(&outpath).map_err(|e| format!("Failed to create file: {}", e))?; - std::io::copy(&mut file, &mut outfile).map_err(|e| format!("Failed to copy file: {}", e))?; + let mut outfile = + fs::File::create(&outpath).map_err(|e| format!("Failed to create file: {}", e))?; + std::io::copy(&mut file, &mut outfile) + .map_err(|e| format!("Failed to copy file: {}", e))?; } } |