@@ -5,8 +5,8 @@ use indicatif::{ProgressBar, ProgressStyle}; |
| 5 | use std::path::PathBuf; | 5 | use std::path::PathBuf; |
| 6 | use wanda_core::{ | 6 | use wanda_core::{ |
| 7 | config::WandaConfig, | 7 | config::WandaConfig, |
| 8 | - prefix::PrefixManager, | 8 | + prefix::{PrefixBuilder, PrefixManager}, |
| 9 | - steam::{ProtonManager, SteamInstallation}, | 9 | + steam::{ProtonCompatibility, ProtonManager, SteamInstallation}, |
| 10 | wemod::{WemodDownloader, WemodInstaller}, | 10 | wemod::{WemodDownloader, WemodInstaller}, |
| 11 | Result, WandaError, | 11 | Result, WandaError, |
| 12 | }; | 12 | }; |
@@ -25,6 +25,10 @@ pub struct InitArgs { |
| 25 | #[arg(long)] | 25 | #[arg(long)] |
| 26 | skip_wemod: bool, | 26 | skip_wemod: bool, |
| 27 | | 27 | |
| | 28 | + /// Skip .NET Framework installation (WeMod needs it for trainers) |
| | 29 | + #[arg(long)] |
| | 30 | + skip_dotnet: bool, |
| | 31 | + |
| 28 | /// Force reinitialization even if already set up | 32 | /// Force reinitialization even if already set up |
| 29 | #[arg(long, short)] | 33 | #[arg(long, short)] |
| 30 | force: bool, | 34 | force: bool, |
@@ -66,19 +70,44 @@ pub async fn run(args: InitArgs, config_path: Option<PathBuf>) -> Result<()> { |
| 66 | } | 70 | } |
| 67 | | 71 | |
| 68 | // Select Proton version | 72 | // Select Proton version |
| | 73 | + // Priority: 1) --proton arg, 2) config preferred_version, 3) get_recommended() |
| | 74 | + // Experimental/Unsupported versions from config are auto-overridden unless --proton is explicit |
| 69 | let proton = if let Some(ref name) = args.proton { | 75 | let proton = if let Some(ref name) = args.proton { |
| 70 | - proton_manager.find_by_name(name).ok_or_else(|| { | 76 | + let v = proton_manager.find_by_name(name).ok_or_else(|| { |
| 71 | eprintln!("Available Proton versions:"); | 77 | eprintln!("Available Proton versions:"); |
| 72 | for v in &proton_manager.versions { | 78 | for v in &proton_manager.versions { |
| 73 | - eprintln!(" - {}", v.name); | 79 | + eprintln!(" - {} ({})", v.name, v.compatibility); |
| 74 | } | 80 | } |
| 75 | WandaError::ProtonNotFound | 81 | WandaError::ProtonNotFound |
| 76 | - })? | 82 | + })?; |
| | 83 | + if matches!(v.compatibility, ProtonCompatibility::Experimental | ProtonCompatibility::Unsupported) { |
| | 84 | + eprintln!( |
| | 85 | + " Warning: '{}' is {} — this may cause WeMod to fail", |
| | 86 | + v.name, v.compatibility |
| | 87 | + ); |
| | 88 | + } |
| | 89 | + v |
| | 90 | + } else if let Some(ref preferred) = config.proton.preferred_version { |
| | 91 | + match proton_manager.find_by_name(preferred) { |
| | 92 | + Some(v) if matches!(v.compatibility, ProtonCompatibility::Experimental | ProtonCompatibility::Unsupported) => { |
| | 93 | + let recommended = proton_manager.get_recommended().ok_or(WandaError::ProtonNotFound)?; |
| | 94 | + eprintln!( |
| | 95 | + " Configured Proton '{}' is {} — auto-switching to '{}' ({})", |
| | 96 | + v.name, v.compatibility, recommended.name, recommended.compatibility |
| | 97 | + ); |
| | 98 | + recommended |
| | 99 | + } |
| | 100 | + Some(v) => v, |
| | 101 | + None => { |
| | 102 | + eprintln!(" Configured Proton '{}' not found, using recommended", preferred); |
| | 103 | + proton_manager.get_recommended().ok_or(WandaError::ProtonNotFound)? |
| | 104 | + } |
| | 105 | + } |
| 77 | } else { | 106 | } else { |
| 78 | proton_manager.get_recommended().ok_or(WandaError::ProtonNotFound)? | 107 | proton_manager.get_recommended().ok_or(WandaError::ProtonNotFound)? |
| 79 | }; | 108 | }; |
| 80 | | 109 | |
| 81 | - println!(" Using: {} ({:?})", proton.name, proton.compatibility); | 110 | + println!(" Using: {} ({})", proton.name, proton.compatibility); |
| 82 | | 111 | |
| 83 | // Set up prefix | 112 | // Set up prefix |
| 84 | println!("\nSetting up Wine prefix..."); | 113 | println!("\nSetting up Wine prefix..."); |
@@ -109,6 +138,36 @@ pub async fn run(args: InitArgs, config_path: Option<PathBuf>) -> Result<()> { |
| 109 | pb.finish_with_message("Prefix created successfully"); | 138 | pb.finish_with_message("Prefix created successfully"); |
| 110 | } | 139 | } |
| 111 | | 140 | |
| | 141 | + // Install .NET Framework 4.8 (required by WeMod's trainer engine) |
| | 142 | + if !args.skip_dotnet { |
| | 143 | + println!("\nInstalling .NET Framework 4.8..."); |
| | 144 | + println!(" This may take 10-15 minutes. Use --skip-dotnet to skip."); |
| | 145 | + |
| | 146 | + let prefix_path = prefix_manager.base_path.join("default"); |
| | 147 | + let dotnet_builder = PrefixBuilder::new(&prefix_path, proton); |
| | 148 | + |
| | 149 | + let pb = ProgressBar::new_spinner(); |
| | 150 | + pb.set_style( |
| | 151 | + ProgressStyle::default_spinner() |
| | 152 | + .template("{spinner:.green} {msg}") |
| | 153 | + .unwrap(), |
| | 154 | + ); |
| | 155 | + pb.set_message("Installing .NET Framework 4.8 via winetricks..."); |
| | 156 | + pb.enable_steady_tick(std::time::Duration::from_millis(100)); |
| | 157 | + |
| | 158 | + match dotnet_builder.install_dotnet().await { |
| | 159 | + Ok(_) => { |
| | 160 | + pb.finish_with_message(".NET Framework installed successfully"); |
| | 161 | + } |
| | 162 | + Err(e) => { |
| | 163 | + pb.finish_with_message(".NET Framework installation failed"); |
| | 164 | + eprintln!(" Warning: .NET install failed: {}", e); |
| | 165 | + eprintln!(" WeMod may show a '.NET framework' error on startup."); |
| | 166 | + eprintln!(" You can retry manually: winetricks -q dotnet48"); |
| | 167 | + } |
| | 168 | + } |
| | 169 | + } |
| | 170 | + |
| 112 | // Reload prefix after creation | 171 | // Reload prefix after creation |
| 113 | prefix_manager.load()?; | 172 | prefix_manager.load()?; |
| 114 | let prefix = prefix_manager.get("default").unwrap(); | 173 | let prefix = prefix_manager.get("default").unwrap(); |