// --- 1. 导入所需模块 --- use thiserror::Error; use zip::ZipArchive; use std::fs::{self, File}; use std::io; use std::path::PathBuf; use serde::Serialize; use tauri::Emitter; // --- 2. (新增) 定义事件的有效负载 (Payload) --- // 这个结构体将作为事件发送给 Svelte #[derive(Clone, serde::Serialize)] struct InstallProgress { message: String, percentage: f32, // 0.0 到 100.0 } // --- 3. 错误定义 --- #[derive(Debug, Error)] pub enum InstallError { #[error("IO 错误: {0}")] Io(#[from] std::io::Error), #[error("Zip 解压错误: {0}")] Zip(#[from] zip::result::ZipError), #[error("找不到 data.bin 文件: {0}")] DataFileNotFound(String), } impl Serialize for InstallError { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { serializer.serialize_str(self.to_string().as_ref()) } } #[tauri::command] // --- 5. (修改) 添加 window 参数 --- pub async fn install_game(window: tauri::Window, target_dir: String) -> Result { println!("Rust 后端收到命令:目标目录 {}", target_dir); // --- 6. 核心改动:从文件系统读取 data.bin --- // --- 6a. (新增) 发送启动事件 --- window.emit("install_progress", InstallProgress { message: "安装开始...".into(), percentage: 0.0 }).ok(); // 1. 获取 app.exe 所在的目录 let exe_path = std::env::current_exe()?; let exe_dir = exe_path.parent() .ok_or_else(|| InstallError::Io(io::Error::new(io::ErrorKind::NotFound, "无法获取执行文件所在目录")))?; // 2. 构造 data.bin 的路径 let data_bin_path = exe_dir.join("data.bin"); println!("正在获取游戏资源包: {:?}", data_bin_path); window.emit("install_progress", InstallProgress { message: "正在获取游戏资源包".into(), percentage: 1.0 // 1% }).ok(); if !data_bin_path.exists() { // 返回错误,Svelte 的 catch 块会捕获它 return Err(InstallError::DataFileNotFound( format!("未在 {:?} 找到游戏资源包,请重新下载", data_bin_path) )); } // 3. 从硬盘打开文件 let archive_file = File::open(&data_bin_path)?; println!("成功打开游戏资源包"); window.emit("install_progress", InstallProgress { message: "成功打开游戏资源包".into(), percentage: 5.0 // 5% }).ok(); // 4. ZipArchive 现在读取文件 let mut archive = ZipArchive::new(archive_file)?; let total_files = archive.len() as f32; println!("在游戏资源包中找到 {} 个文件。", total_files); window.emit("install_progress", InstallProgress { message: format!("包中找到 {} 个文件。", total_files), percentage: 10.0 // 10% }).ok(); // --- 7. 解压循环 (核心改动: 添加 emit) --- let install_path = PathBuf::from(target_dir); fs::create_dir_all(&install_path)?; for i in 0..archive.len() { let mut file = archive.by_index(i)?; let outpath = match file.enclosed_name() { Some(path) => install_path.join(path), None => continue, }; // --- 7a. (核心) 计算百分比并发送事件 --- let current_file_index = i as f32; let percentage = 10.0 + (current_file_index / total_files) * 85.0; // (10% + 85% = 95%) let message = format!("安装中 [{}/{}]: {}", i + 1, total_files as u32, file.name()); window.emit("install_progress", InstallProgress { message, percentage }) .ok(); // --- 2. (新增) 关键修复 --- // 释放线程 1 毫秒,给 Svelte 留出渲染时间 // sleep(Duration::from_millis(1)).await; if (&*file.name()).ends_with('/') { fs::create_dir_all(&outpath)?; } else { if let Some(p) = outpath.parent() { if !p.exists() { fs::create_dir_all(p)?; } } let mut outfile = File::create(&outpath)?; io::copy(&mut file, &mut outfile)?; } } // --- 循环结束 --- println!("Rust 后端:解压成功。"); window.emit("install_progress", InstallProgress { message: "安装成功".into(), percentage: 100.0 // 100% }).ok(); Ok(format!("成功解压 {} 个文件到指定目录!", total_files as u32)) }