diff --git a/packages/ui/src/components/config-editor.tsx b/packages/ui/src/components/config-editor.tsx index 129b8f7..1865067 100644 --- a/packages/ui/src/components/config-editor.tsx +++ b/packages/ui/src/components/config-editor.tsx @@ -1,6 +1,7 @@ import type React from "react"; import { useEffect, useState } from "react"; import { type ZodType, z } from "zod"; +import { MAX_DOWNLOAD_THREADS, MIN_DOWNLOAD_THREADS } from "@/lib/config"; import { useSettingsStore } from "@/models/settings"; import type { LauncherConfig } from "@/types"; import { Button } from "./ui/button"; @@ -22,7 +23,10 @@ const launcherConfigSchema: ZodType = z.object({ javaPath: z.string(), width: z.number(), height: z.number(), - downloadThreads: z.number(), + downloadThreads: z + .number() + .min(MIN_DOWNLOAD_THREADS) + .max(MAX_DOWNLOAD_THREADS), customBackgroundPath: z.string().nullable(), enableGpuAcceleration: z.boolean(), enableVisualEffects: z.boolean(), diff --git a/packages/ui/src/lib/config.ts b/packages/ui/src/lib/config.ts new file mode 100644 index 0000000..ef24327 --- /dev/null +++ b/packages/ui/src/lib/config.ts @@ -0,0 +1,2 @@ +export const MIN_DOWNLOAD_THREADS = 1; +export const MAX_DOWNLOAD_THREADS = 64; diff --git a/packages/ui/src/pages/settings.tsx b/packages/ui/src/pages/settings.tsx index 9387e23..208c102 100644 --- a/packages/ui/src/pages/settings.tsx +++ b/packages/ui/src/pages/settings.tsx @@ -31,6 +31,7 @@ import { import { Spinner } from "@/components/ui/spinner"; import { Switch } from "@/components/ui/switch"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { MAX_DOWNLOAD_THREADS, MIN_DOWNLOAD_THREADS } from "@/lib/config"; import { useJavaStore } from "@/models/java"; import { useSettingsStore } from "@/models/settings"; @@ -168,8 +169,8 @@ export function SettingsPage() { onBlur={() => { settings.save(); }} - min={1} - max={64} + min={MIN_DOWNLOAD_THREADS} + max={MAX_DOWNLOAD_THREADS} /> diff --git a/src-tauri/src/core/config.rs b/src-tauri/src/core/config.rs index d1f306a..417b774 100644 --- a/src-tauri/src/core/config.rs +++ b/src-tauri/src/core/config.rs @@ -5,6 +5,9 @@ use std::sync::Mutex; use tauri::{AppHandle, Manager}; use ts_rs::TS; +pub const MIN_DOWNLOAD_THREADS: u32 = 1; +pub const MAX_DOWNLOAD_THREADS: u32 = 64; + #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] #[ts(export, export_to = "config.ts")] @@ -85,7 +88,8 @@ pub struct LauncherConfig { pub java_path: String, pub width: u32, pub height: u32, - pub download_threads: u32, // concurrent download threads (1-128) + /// Concurrent download threads. Clamped via MIN_DOWNLOAD_THREADS/MAX_DOWNLOAD_THREADS. + pub download_threads: u32, pub custom_background_path: Option, pub enable_gpu_acceleration: bool, pub enable_visual_effects: bool, @@ -109,7 +113,7 @@ impl Default for LauncherConfig { java_path: "java".to_string(), width: 854, height: 480, - download_threads: 32, + download_threads: 8, custom_background_path: None, enable_gpu_acceleration: false, enable_visual_effects: true, @@ -125,6 +129,14 @@ impl Default for LauncherConfig { } } +impl LauncherConfig { + pub fn sanitize(&mut self) { + self.download_threads = + self.download_threads + .clamp(MIN_DOWNLOAD_THREADS, MAX_DOWNLOAD_THREADS); + } +} + pub struct ConfigState { pub config: Mutex, pub file_path: PathBuf, @@ -137,7 +149,9 @@ impl ConfigState { 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() + let mut config: LauncherConfig = serde_json::from_str(&content).unwrap_or_default(); + config.sanitize(); + config } else { LauncherConfig::default() }; @@ -150,7 +164,8 @@ impl ConfigState { pub fn save(&self) -> Result<(), String> { let config = self.config.lock().unwrap(); - let content = serde_json::to_string_pretty(&*config).map_err(|e| e.to_string())?; + let content = serde_json::to_string_pretty(&*config) + .map_err(|e| format!("Failed to serialize config: {}", e))?; fs::create_dir_all(self.file_path.parent().unwrap()).map_err(|e| e.to_string())?; fs::write(&self.file_path, content).map_err(|e| e.to_string())?; Ok(()) diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 952d250..fec8f11 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1646,11 +1646,15 @@ async fn save_raw_config( content: String, ) -> Result<(), String> { // Validate JSON - let new_config: core::config::LauncherConfig = + let mut new_config: core::config::LauncherConfig = serde_json::from_str(&content).map_err(|e| format!("Invalid JSON: {}", e))?; + new_config.sanitize(); + + let normalized_content = serde_json::to_string_pretty(&new_config) + .map_err(|e| format!("Failed to serialize config: {}", e))?; // Save to file - tokio::fs::write(&state.file_path, &content) + tokio::fs::write(&state.file_path, &normalized_content) .await .map_err(|e| e.to_string())?;