From cd82fabfac3120179f947fba331025676dc1af7f Mon Sep 17 00:00:00 2001 From: Natsuu Date: Wed, 14 Jan 2026 04:40:03 +0000 Subject: feat: add functionality to retrieve installed game versions --- src-tauri/src/main.rs | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) (limited to 'src-tauri/src') diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index d7ae9a4..bf7504d 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -586,6 +586,42 @@ async fn get_versions() -> Result, String> { } } +#[tauri::command] +async fn get_installed_versions(app_handle: tauri::AppHandle) -> Result, String> { + let game_dir = app_handle + .path() + .app_data_dir() + .map_err(|e| format!("Failed to get app data dir: {}", e))?; + + let versions_dir = game_dir.join("versions"); + + if !versions_dir.exists() { + return Ok(Vec::new()); + } + + let mut installed_versions = Vec::new(); + + if let Ok(entries) = std::fs::read_dir(versions_dir) { + for entry in entries { + if let Ok(entry) = entry { + if let Ok(file_type) = entry.file_type() { + if file_type.is_dir() { + if let Ok(file_name) = entry.file_name().into_string() { + // Optionally verify if {version_id}.json exists inside + let json_path = entry.path().join(format!("{}.json", file_name)); + if json_path.exists() { + installed_versions.push(file_name); + } + } + } + } + } + } + } + + Ok(installed_versions) +} + #[tauri::command] async fn login_offline( window: Window, @@ -784,6 +820,7 @@ fn main() { .invoke_handler(tauri::generate_handler![ start_game, get_versions, + get_installed_versions, login_offline, get_active_account, logout, -- cgit v1.2.3-70-g09d2 From 11a604f7e03d1a6f1b4ad381fdcf0ccaef440cff Mon Sep 17 00:00:00 2001 From: Natsuu Date: Wed, 14 Jan 2026 04:51:46 +0000 Subject: refactor: simplify get_installed_versions function by using iterator methods --- src-tauri/src/main.rs | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) (limited to 'src-tauri/src') diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index bf7504d..c1b882c 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -595,29 +595,23 @@ async fn get_installed_versions(app_handle: tauri::AppHandle) -> Result Date: Wed, 14 Jan 2026 13:05:38 +0800 Subject: Update src-tauri/src/main.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> --- src-tauri/src/main.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'src-tauri/src') diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index c1b882c..bb79f3e 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -594,9 +594,21 @@ async fn get_installed_versions(app_handle: tauri::AppHandle) -> Result entries, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + // No versions directory yet; treat as "no versions installed" + return Ok(Vec::new()); + } + Err(e) => { + eprintln!( + "Failed to read versions directory {}: {}", + versions_dir.display(), + e + ); + return Err(format!("Failed to read versions directory: {}", e)); + } }; let installed_versions = entries -- cgit v1.2.3-70-g09d2 From b21df2f9abb9c8d5da0b77f2d3756802f95a1ad2 Mon Sep 17 00:00:00 2001 From: HsiangNianian Date: Wed, 14 Jan 2026 13:35:06 +0800 Subject: feat: display download rate and progress with concurrency support --- src-tauri/src/core/downloader.rs | 33 +++++++++++-- ui/src/lib/DownloadMonitor.svelte | 98 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 124 insertions(+), 7 deletions(-) (limited to 'src-tauri/src') diff --git a/src-tauri/src/core/downloader.rs b/src-tauri/src/core/downloader.rs index 5f6ec80..5a0605b 100644 --- a/src-tauri/src/core/downloader.rs +++ b/src-tauri/src/core/downloader.rs @@ -1,6 +1,7 @@ use futures::StreamExt; use serde::{Deserialize, Serialize}; use std::path::PathBuf; +use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering}; use std::sync::Arc; use tauri::{Emitter, Window}; use tokio::io::AsyncWriteExt; @@ -19,11 +20,20 @@ pub struct ProgressEvent { pub downloaded: u64, pub total: u64, pub status: String, // "Downloading", "Verifying", "Finished", "Error" + pub completed_files: usize, + pub total_files: usize, + pub total_downloaded_bytes: u64, } -pub async fn download_files(window: Window, tasks: Vec) -> Result<(), String> { - let client = reqwest::Client::new(); - let semaphore = Arc::new(Semaphore::new(10)); // Max 10 concurrent downloads +pub async fn download_files(window: Window, tasks: Vec, max_concurrent: usize) -> Result<(), String> { + let client = reqwest::Client::builder() + .pool_max_idle_per_host(max_concurrent) + .build() + .map_err(|e| e.to_string())?; + let semaphore = Arc::new(Semaphore::new(max_concurrent)); + let completed_files = Arc::new(AtomicUsize::new(0)); + let total_downloaded_bytes = Arc::new(AtomicU64::new(0)); + let total_files = tasks.len(); // Notify start (total files) let _ = window.emit("download-start", tasks.len()); @@ -32,6 +42,8 @@ pub async fn download_files(window: Window, tasks: Vec) -> Result< let client = client.clone(); let window = window.clone(); let semaphore = semaphore.clone(); + let completed_files = completed_files.clone(); + let total_downloaded_bytes = total_downloaded_bytes.clone(); async move { let _permit = semaphore.acquire().await.unwrap(); @@ -46,6 +58,9 @@ pub async fn download_files(window: Window, tasks: Vec) -> Result< downloaded: 0, total: 0, status: "Verifying".into(), + completed_files: completed_files.load(Ordering::Relaxed), + total_files, + total_downloaded_bytes: total_downloaded_bytes.load(Ordering::Relaxed), }, ); @@ -57,6 +72,7 @@ pub async fn download_files(window: Window, tasks: Vec) -> Result< let result = hex::encode(hasher.finalize()); if &result == expected_sha1 { // Already valid + let completed = completed_files.fetch_add(1, Ordering::Relaxed) + 1; let _ = window.emit( "download-progress", ProgressEvent { @@ -64,6 +80,9 @@ pub async fn download_files(window: Window, tasks: Vec) -> Result< downloaded: 0, total: 0, status: "Skipped".into(), + completed_files: completed, + total_files, + total_downloaded_bytes: total_downloaded_bytes.load(Ordering::Relaxed), }, ); return Ok(()); @@ -93,6 +112,7 @@ pub async fn download_files(window: Window, tasks: Vec) -> Result< return Err(format!("Write error: {}", e)); } downloaded += chunk.len() as u64; + let total_bytes = total_downloaded_bytes.fetch_add(chunk.len() as u64, Ordering::Relaxed) + chunk.len() as u64; let _ = window.emit( "download-progress", ProgressEvent { @@ -100,6 +120,9 @@ pub async fn download_files(window: Window, tasks: Vec) -> Result< downloaded, total: total_size, status: "Downloading".into(), + completed_files: completed_files.load(Ordering::Relaxed), + total_files, + total_downloaded_bytes: total_bytes, }, ); } @@ -111,6 +134,7 @@ pub async fn download_files(window: Window, tasks: Vec) -> Result< Err(e) => return Err(format!("Request error: {}", e)), } + let completed = completed_files.fetch_add(1, Ordering::Relaxed) + 1; let _ = window.emit( "download-progress", ProgressEvent { @@ -118,6 +142,9 @@ pub async fn download_files(window: Window, tasks: Vec) -> Result< downloaded: 0, total: 0, status: "Finished".into(), + completed_files: completed, + total_files, + total_downloaded_bytes: total_downloaded_bytes.load(Ordering::Relaxed), }, ); diff --git a/ui/src/lib/DownloadMonitor.svelte b/ui/src/lib/DownloadMonitor.svelte index b796591..e4920ad 100644 --- a/ui/src/lib/DownloadMonitor.svelte +++ b/ui/src/lib/DownloadMonitor.svelte @@ -9,11 +9,16 @@ downloaded: number; // in bytes total: number; // in bytes status: string; + completed_files: number; + total_files: number; + total_downloaded_bytes: number; } let currentFile = ""; - let progress = 0; // percentage 0-100 + let progress = 0; // percentage 0-100 (current file) + let totalProgress = 0; // percentage 0-100 (all files) let totalFiles = 0; + let completedFiles = 0; let statusText = "Preparing..."; let unlistenProgress: () => void; let unlistenStart: () => void; @@ -21,13 +26,30 @@ let downloadedBytes = 0; let totalBytes = 0; + // Speed and ETA tracking + let downloadSpeed = 0; // bytes per second + let etaSeconds = 0; + let startTime = 0; + let totalDownloadedBytes = 0; + let lastUpdateTime = 0; + let lastTotalBytes = 0; + onMount(async () => { unlistenStart = await listen("download-start", (event) => { visible = true; totalFiles = event.payload; + completedFiles = 0; progress = 0; + totalProgress = 0; statusText = "Starting download..."; currentFile = ""; + // Reset speed tracking + startTime = Date.now(); + totalDownloadedBytes = 0; + downloadSpeed = 0; + etaSeconds = 0; + lastUpdateTime = Date.now(); + lastTotalBytes = 0; }); unlistenProgress = await listen( @@ -36,8 +58,7 @@ const payload = event.payload; currentFile = payload.file; - // Simple file progress for now. Global progress would require tracking all files. - // For single file (Client jar), this is accurate. + // Current file progress downloadedBytes = payload.downloaded; totalBytes = payload.total; @@ -46,12 +67,45 @@ if (payload.total > 0) { progress = (payload.downloaded / payload.total) * 100; } + + // Total progress (all files) + completedFiles = payload.completed_files; + totalFiles = payload.total_files; + if (totalFiles > 0) { + totalProgress = (completedFiles / totalFiles) * 100; + } + + // Calculate download speed (using moving average) + totalDownloadedBytes = payload.total_downloaded_bytes; + const now = Date.now(); + const timeDiff = (now - lastUpdateTime) / 1000; // seconds + + if (timeDiff >= 0.5) { // Update speed every 0.5 seconds + const bytesDiff = totalDownloadedBytes - lastTotalBytes; + const instantSpeed = bytesDiff / timeDiff; + // Smooth the speed with exponential moving average + downloadSpeed = downloadSpeed === 0 ? instantSpeed : downloadSpeed * 0.7 + instantSpeed * 0.3; + lastUpdateTime = now; + lastTotalBytes = totalDownloadedBytes; + } + + // Estimate remaining time based on files remaining + if (downloadSpeed > 0 && completedFiles < totalFiles) { + // Rough estimate: assume average file size based on downloaded data + const avgBytesPerFile = completedFiles > 0 ? totalDownloadedBytes / completedFiles : totalDownloadedBytes; + const remainingFiles = totalFiles - completedFiles; + const estimatedRemainingBytes = avgBytesPerFile * remainingFiles; + etaSeconds = estimatedRemainingBytes / downloadSpeed; + } else { + etaSeconds = 0; + } } ); unlistenComplete = await listen("download-complete", () => { statusText = "Done!"; progress = 100; + totalProgress = 100; setTimeout(() => { visible = false; }, 2000); @@ -71,6 +125,24 @@ const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; } + + function formatSpeed(bytesPerSecond: number) { + if (bytesPerSecond === 0) return "-- /s"; + return formatBytes(bytesPerSecond) + "/s"; + } + + function formatTime(seconds: number) { + if (seconds <= 0 || !isFinite(seconds)) return "--"; + if (seconds < 60) return `${Math.round(seconds)}s`; + if (seconds < 3600) { + const mins = Math.floor(seconds / 60); + const secs = Math.round(seconds % 60); + return `${mins}m ${secs}s`; + } + const hours = Math.floor(seconds / 3600); + const mins = Math.floor((seconds % 3600) / 60); + return `${hours}h ${mins}m`; + } {#if visible} @@ -82,11 +154,29 @@ {statusText} + +
+
+ Total Progress + {completedFiles} / {totalFiles} files +
+
+
+
+
+ {formatSpeed(downloadSpeed)} · ETA: {formatTime(etaSeconds)} + {completedFiles < totalFiles ? Math.floor(totalProgress) : 100}% +
+
+
{currentFile || "Waiting..."}
- +
Date: Wed, 14 Jan 2026 13:35:29 +0800 Subject: feat: add download_threads to LauncherConfig and update download logging --- src-tauri/src/core/config.rs | 2 ++ src-tauri/src/main.rs | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'src-tauri/src') diff --git a/src-tauri/src/core/config.rs b/src-tauri/src/core/config.rs index 47c5306..dc72dcb 100644 --- a/src-tauri/src/core/config.rs +++ b/src-tauri/src/core/config.rs @@ -11,6 +11,7 @@ pub struct LauncherConfig { pub java_path: String, pub width: u32, pub height: u32, + pub download_threads: u32, // concurrent download threads } impl Default for LauncherConfig { @@ -21,6 +22,7 @@ impl Default for LauncherConfig { java_path: "java".to_string(), width: 854, height: 480, + download_threads: 32, } } } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index d7ae9a4..73310d5 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -262,8 +262,8 @@ async fn start_game( )); // 4. Start Download - emit_log!(window, "Starting downloads...".to_string()); - core::downloader::download_files(window.clone(), download_tasks) + emit_log!(window, format!("Starting downloads with {} concurrent threads...", config.download_threads)); + core::downloader::download_files(window.clone(), download_tasks, config.download_threads as usize) .await .map_err(|e| e.to_string())?; emit_log!(window, "All downloads completed successfully".to_string()); -- cgit v1.2.3-70-g09d2 From df1450d565fda97e6c6dfce825abb682f567759b Mon Sep 17 00:00:00 2001 From: HsiangNianian Date: Wed, 14 Jan 2026 13:44:01 +0800 Subject: fix: update download_threads comment to specify valid range (1-128) --- src-tauri/src/core/config.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src-tauri/src') diff --git a/src-tauri/src/core/config.rs b/src-tauri/src/core/config.rs index dc72dcb..d6d594f 100644 --- a/src-tauri/src/core/config.rs +++ b/src-tauri/src/core/config.rs @@ -5,13 +5,14 @@ use std::sync::Mutex; use tauri::{AppHandle, Manager}; #[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(default)] pub struct LauncherConfig { pub min_memory: u32, // in MB pub max_memory: u32, // in MB pub java_path: String, pub width: u32, pub height: u32, - pub download_threads: u32, // concurrent download threads + pub download_threads: u32, // concurrent download threads (1-128) } impl Default for LauncherConfig { -- cgit v1.2.3-70-g09d2 From 188e3a910ce3566742068979d7fc1eb6f454884c Mon Sep 17 00:00:00 2001 From: HsiangNianian Date: Wed, 14 Jan 2026 13:44:06 +0800 Subject: feat: implement global progress tracking for concurrent downloads --- src-tauri/src/core/downloader.rs | 147 +++++++++++++++++++++++---------------- 1 file changed, 88 insertions(+), 59 deletions(-) (limited to 'src-tauri/src') diff --git a/src-tauri/src/core/downloader.rs b/src-tauri/src/core/downloader.rs index 5a0605b..139b621 100644 --- a/src-tauri/src/core/downloader.rs +++ b/src-tauri/src/core/downloader.rs @@ -25,15 +25,92 @@ pub struct ProgressEvent { pub total_downloaded_bytes: u64, } +/// Snapshot of global progress state +struct ProgressSnapshot { + completed_files: usize, + total_files: usize, + total_downloaded_bytes: u64, +} + +/// Centralized progress tracking with atomic counters +struct GlobalProgress { + completed_files: AtomicUsize, + total_downloaded_bytes: AtomicU64, + total_files: usize, +} + +impl GlobalProgress { + fn new(total_files: usize) -> Self { + Self { + completed_files: AtomicUsize::new(0), + total_downloaded_bytes: AtomicU64::new(0), + total_files, + } + } + + /// Get current progress snapshot without modification + fn snapshot(&self) -> ProgressSnapshot { + ProgressSnapshot { + completed_files: self.completed_files.load(Ordering::Relaxed), + total_files: self.total_files, + total_downloaded_bytes: self.total_downloaded_bytes.load(Ordering::Relaxed), + } + } + + /// Increment completed files counter and return updated snapshot + fn inc_completed(&self) -> ProgressSnapshot { + let completed = self.completed_files.fetch_add(1, Ordering::Relaxed) + 1; + ProgressSnapshot { + completed_files: completed, + total_files: self.total_files, + total_downloaded_bytes: self.total_downloaded_bytes.load(Ordering::Relaxed), + } + } + + /// Add downloaded bytes and return updated snapshot + fn add_bytes(&self, delta: u64) -> ProgressSnapshot { + let total_bytes = self.total_downloaded_bytes.fetch_add(delta, Ordering::Relaxed) + delta; + ProgressSnapshot { + completed_files: self.completed_files.load(Ordering::Relaxed), + total_files: self.total_files, + total_downloaded_bytes: total_bytes, + } + } +} + +/// Emit a progress event to the frontend +fn emit_progress( + window: &Window, + file_name: &str, + status: &str, + downloaded: u64, + total: u64, + snapshot: &ProgressSnapshot, +) { + let _ = window.emit( + "download-progress", + ProgressEvent { + file: file_name.to_string(), + downloaded, + total, + status: status.into(), + completed_files: snapshot.completed_files, + total_files: snapshot.total_files, + total_downloaded_bytes: snapshot.total_downloaded_bytes, + }, + ); +} + pub async fn download_files(window: Window, tasks: Vec, max_concurrent: usize) -> Result<(), String> { + // Clamp max_concurrent to a valid range (1-128) to prevent edge cases + let max_concurrent = max_concurrent.clamp(1, 128); + let client = reqwest::Client::builder() .pool_max_idle_per_host(max_concurrent) .build() .map_err(|e| e.to_string())?; let semaphore = Arc::new(Semaphore::new(max_concurrent)); - let completed_files = Arc::new(AtomicUsize::new(0)); - let total_downloaded_bytes = Arc::new(AtomicU64::new(0)); - let total_files = tasks.len(); + let progress = Arc::new(GlobalProgress::new(tasks.len())); // Notify start (total files) let _ = window.emit("download-start", tasks.len()); @@ -42,8 +119,7 @@ pub async fn download_files(window: Window, tasks: Vec, max_concur let client = client.clone(); let window = window.clone(); let semaphore = semaphore.clone(); - let completed_files = completed_files.clone(); - let total_downloaded_bytes = total_downloaded_bytes.clone(); + let progress = progress.clone(); async move { let _permit = semaphore.acquire().await.unwrap(); @@ -51,18 +127,7 @@ pub async fn download_files(window: Window, tasks: Vec, max_concur // 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(), - completed_files: completed_files.load(Ordering::Relaxed), - total_files, - total_downloaded_bytes: total_downloaded_bytes.load(Ordering::Relaxed), - }, - ); + emit_progress(&window, &file_name, "Verifying", 0, 0, &progress.snapshot()); if let Some(expected_sha1) = &task.sha1 { if let Ok(data) = tokio::fs::read(&task.path).await { @@ -71,20 +136,8 @@ pub async fn download_files(window: Window, tasks: Vec, max_concur hasher.update(&data); let result = hex::encode(hasher.finalize()); if &result == expected_sha1 { - // Already valid - let completed = completed_files.fetch_add(1, Ordering::Relaxed) + 1; - let _ = window.emit( - "download-progress", - ProgressEvent { - file: file_name.clone(), - downloaded: 0, - total: 0, - status: "Skipped".into(), - completed_files: completed, - total_files, - total_downloaded_bytes: total_downloaded_bytes.load(Ordering::Relaxed), - }, - ); + // Already valid, skip download + emit_progress(&window, &file_name, "Skipped", 0, 0, &progress.inc_completed()); return Ok(()); } } @@ -112,19 +165,8 @@ pub async fn download_files(window: Window, tasks: Vec, max_concur return Err(format!("Write error: {}", e)); } downloaded += chunk.len() as u64; - let total_bytes = total_downloaded_bytes.fetch_add(chunk.len() as u64, Ordering::Relaxed) + chunk.len() as u64; - let _ = window.emit( - "download-progress", - ProgressEvent { - file: file_name.clone(), - downloaded, - total: total_size, - status: "Downloading".into(), - completed_files: completed_files.load(Ordering::Relaxed), - total_files, - total_downloaded_bytes: total_bytes, - }, - ); + let snapshot = progress.add_bytes(chunk.len() as u64); + emit_progress(&window, &file_name, "Downloading", downloaded, total_size, &snapshot); } Ok(None) => break, Err(e) => return Err(format!("Download error: {}", e)), @@ -134,27 +176,14 @@ pub async fn download_files(window: Window, tasks: Vec, max_concur Err(e) => return Err(format!("Request error: {}", e)), } - let completed = completed_files.fetch_add(1, Ordering::Relaxed) + 1; - let _ = window.emit( - "download-progress", - ProgressEvent { - file: file_name.clone(), - downloaded: 0, - total: 0, - status: "Finished".into(), - completed_files: completed, - total_files, - total_downloaded_bytes: total_downloaded_bytes.load(Ordering::Relaxed), - }, - ); - + emit_progress(&window, &file_name, "Finished", 0, 0, &progress.inc_completed()); Ok(()) } }); // Buffer unordered to run concurrently tasks_stream - .buffer_unordered(10) + .buffer_unordered(max_concurrent) .collect::>>() .await; -- cgit v1.2.3-70-g09d2 From ee44423edfa83597411d6f499ae61c2aa2d9ad1f Mon Sep 17 00:00:00 2001 From: 简律纯 Date: Wed, 14 Jan 2026 14:13:01 +0800 Subject: Update src-tauri/src/core/downloader.rs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src-tauri/src/core/downloader.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'src-tauri/src') diff --git a/src-tauri/src/core/downloader.rs b/src-tauri/src/core/downloader.rs index 139b621..d792167 100644 --- a/src-tauri/src/core/downloader.rs +++ b/src-tauri/src/core/downloader.rs @@ -105,10 +105,7 @@ pub async fn download_files(window: Window, tasks: Vec, max_concur // Clamp max_concurrent to a valid range (1-128) to prevent edge cases let max_concurrent = max_concurrent.clamp(1, 128); - let client = reqwest::Client::builder() - .pool_max_idle_per_host(max_concurrent) - .build() - .map_err(|e| e.to_string())?; + let client = reqwest::Client::new(); let semaphore = Arc::new(Semaphore::new(max_concurrent)); let progress = Arc::new(GlobalProgress::new(tasks.len())); -- cgit v1.2.3-70-g09d2 From bf0a493020aa2e0ffea47e0d10105dd525439f23 Mon Sep 17 00:00:00 2001 From: 简律纯 Date: Wed, 14 Jan 2026 14:13:14 +0800 Subject: Update src-tauri/src/core/downloader.rs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src-tauri/src/core/downloader.rs | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src-tauri/src') diff --git a/src-tauri/src/core/downloader.rs b/src-tauri/src/core/downloader.rs index d792167..7ff81ad 100644 --- a/src-tauri/src/core/downloader.rs +++ b/src-tauri/src/core/downloader.rs @@ -134,6 +134,13 @@ pub async fn download_files(window: Window, tasks: Vec, max_concur let result = hex::encode(hasher.finalize()); if &result == expected_sha1 { // Already valid, skip download + let skipped_size = tokio::fs::metadata(&task.path) + .await + .map(|m| m.len()) + .unwrap_or(0); + if skipped_size > 0 { + let _ = progress.add_bytes(skipped_size); + } emit_progress(&window, &file_name, "Skipped", 0, 0, &progress.inc_completed()); return Ok(()); } -- cgit v1.2.3-70-g09d2 From 21c66d00d8d300b33a353a366fa23d0773deb413 Mon Sep 17 00:00:00 2001 From: 简律纯 Date: Wed, 14 Jan 2026 14:19:15 +0800 Subject: Revert "feat: add functionality to retrieve installed game versions" --- src-tauri/src/main.rs | 43 -------------------------------------- ui/src/components/BottomBar.svelte | 7 ++----- ui/src/stores/game.svelte.ts | 32 ++++------------------------ 3 files changed, 6 insertions(+), 76 deletions(-) (limited to 'src-tauri/src') diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 53d9388..73310d5 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -586,48 +586,6 @@ async fn get_versions() -> Result, String> { } } -#[tauri::command] -async fn get_installed_versions(app_handle: tauri::AppHandle) -> Result, String> { - let game_dir = app_handle - .path() - .app_data_dir() - .map_err(|e| format!("Failed to get app data dir: {}", e))?; - - let versions_dir = game_dir.join("versions"); - - let entries = match std::fs::read_dir(&versions_dir) { - Ok(entries) => entries, - Err(e) if e.kind() == std::io::ErrorKind::NotFound => { - // No versions directory yet; treat as "no versions installed" - return Ok(Vec::new()); - } - Err(e) => { - eprintln!( - "Failed to read versions directory {}: {}", - versions_dir.display(), - e - ); - return Err(format!("Failed to read versions directory: {}", e)); - } - }; - - let installed_versions = entries - .flatten() - .filter(|entry| entry.file_type().map(|ft| ft.is_dir()).unwrap_or(false)) - .filter_map(|entry| { - let file_name = entry.file_name().into_string().ok()?; - let json_path = entry.path().join(format!("{}.json", file_name)); - if json_path.exists() { - Some(file_name) - } else { - None - } - }) - .collect(); - - Ok(installed_versions) -} - #[tauri::command] async fn login_offline( window: Window, @@ -826,7 +784,6 @@ fn main() { .invoke_handler(tauri::generate_handler![ start_game, get_versions, - get_installed_versions, login_offline, get_active_account, logout, diff --git a/ui/src/components/BottomBar.svelte b/ui/src/components/BottomBar.svelte index a96b086..dcad9e8 100644 --- a/ui/src/components/BottomBar.svelte +++ b/ui/src/components/BottomBar.svelte @@ -65,13 +65,10 @@ > {#if gameState.versions.length === 0} - {:else if gameState.installedVersionIds.length === 0} - {:else} {#each gameState.versions as version} - {#if gameState.installedVersionIds.includes(version.id)} - - {/if} + {/each} {/if} diff --git a/ui/src/stores/game.svelte.ts b/ui/src/stores/game.svelte.ts index f66cc71..0af3daf 100644 --- a/ui/src/stores/game.svelte.ts +++ b/ui/src/stores/game.svelte.ts @@ -5,38 +5,14 @@ import { authState } from "./auth.svelte"; export class GameState { versions = $state([]); - installedVersionIds = $state([]); selectedVersion = $state(""); async loadVersions() { try { - // Fetch both full version list and installed versions - const [allVersions, installedIds] = await Promise.all([ - invoke("get_versions"), - invoke("get_installed_versions") - ]); - - this.versions = allVersions; - this.installedVersionIds = installedIds; - - if (this.installedVersionIds.length > 0) { - // Find the first installed version that appears in our manifest (preserving order) - // Usually we want the latest release that is installed - const installedVersions = this.versions.filter(v => this.installedVersionIds.includes(v.id)); - - // Try to find latest release among installed - const latestInstalledRelease = installedVersions.find(v => v.type === "release"); - - if (latestInstalledRelease) { - this.selectedVersion = latestInstalledRelease.id; - } else if (installedVersions.length > 0) { - this.selectedVersion = installedVersions[0].id; - } else { - // Fallback to just the first ID if not in manifest - this.selectedVersion = this.installedVersionIds[0]; - } - } else { - this.selectedVersion = ""; + this.versions = await invoke("get_versions"); + if (this.versions.length > 0) { + const latest = this.versions.find((v) => v.type === "release"); + this.selectedVersion = latest ? latest.id : this.versions[0].id; } } catch (e) { console.error("Failed to fetch versions:", e); -- cgit v1.2.3-70-g09d2