aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorHsiangNianian <i@jyunko.cn>2026-01-13 17:54:42 +0800
committerHsiangNianian <i@jyunko.cn>2026-01-13 17:54:42 +0800
commit50f4725c657c6583452d9c85f9b934c3cf2a0f70 (patch)
tree8d8de216b134a04507d68dd2dc7516b8dbe433bd
parentf7cabe5a0388518be50eb225500b7b4800109274 (diff)
downloadDropOut-50f4725c657c6583452d9c85f9b934c3cf2a0f70.tar.gz
DropOut-50f4725c657c6583452d9c85f9b934c3cf2a0f70.zip
chore(style): format code
-rw-r--r--src-tauri/build.rs2
-rw-r--r--src-tauri/src/core/config.rs2
-rw-r--r--src-tauri/src/core/downloader.rs99
-rw-r--r--src-tauri/src/core/game_version.rs4
-rw-r--r--src-tauri/src/core/mod.rs4
-rw-r--r--src-tauri/src/core/rules.rs14
-rw-r--r--src-tauri/src/launcher/config.rs2
-rw-r--r--src-tauri/src/launcher/launcher.rs7
-rw-r--r--src-tauri/src/launcher/mod.rs2
-rw-r--r--src-tauri/src/main.rs360
-rw-r--r--src-tauri/src/utils/mod.rs2
-rw-r--r--src-tauri/src/utils/zip.rs20
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))?;
}
}