feat: wire up interactive GUI and CLI subcommands to launcher, cleaner, and region router

This commit is contained in:
katR 2026-10-01 22:48:21 +05:30
parent 2fcc4dd995
commit 66deae64d6
4 changed files with 127 additions and 4 deletions

View file

@ -16,6 +16,8 @@ pub enum Commands {
place_id: Option<u64>,
#[arg(long)]
safe_mode: bool,
#[arg(short, long)]
region: Option<String>,
},
/// Check for and download the latest Roblox version
Update,

View file

@ -3,8 +3,12 @@ pub mod commands;
use clap::Parser;
use commands::{Cli, Commands};
use molotov_bootstrap::cdn::CdnClient;
use molotov_bootstrap::cleaner::Cleaner;
use molotov_bootstrap::fflags::FastFlagManager;
use molotov_bootstrap::launcher::{LaunchOptions, Launcher};
use molotov_bootstrap::region::ServerRegion;
use molotov_bootstrap::sandbox::SandboxRunner;
use std::path::PathBuf;
pub fn run(args: &[String]) {
let mut full_args = vec!["molotov".to_string()];
@ -18,12 +22,45 @@ pub fn run(args: &[String]) {
}
};
let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
let base_dir = PathBuf::from(&home).join(".local/share/molotov");
let downloads_dir = PathBuf::from(&home).join(".cache/molotov/downloads");
let versions_dir = base_dir.join("versions");
match cli.command {
Some(Commands::Launch { place_id, safe_mode }) => {
println!("🚀 Preparing Molotov launch (Place: {:?}, Safe Mode: {})...", place_id, safe_mode);
Some(Commands::Launch { place_id, safe_mode, region }) => {
let s_region = region
.as_deref()
.map(ServerRegion::from_code)
.unwrap_or(ServerRegion::Auto);
println!("🚀 Preparing Molotov launch...");
println!(" • Target Place ID: {:?}", place_id);
println!(" • Safe Mode: {}", safe_mode);
println!(" • Preferred Server Region: {}", s_region.display_name());
if let Err(e) = SandboxRunner::check_bwrap_installed() {
eprintln!("⚠️ Warning: {}", e);
}
let launcher = Launcher::new(base_dir);
let options = LaunchOptions {
place_id,
safe_mode,
region: s_region,
instance_id: "default".to_string(),
};
match launcher.prepare_launch(options) {
Ok(plan) => {
println!("✅ Launch plan prepared successfully.");
println!(" • FastFlags configured at: {}", plan.fflags_path.display());
println!(" • Sandbox isolation: Instance '{}'", plan.sandbox_config.instance_id);
}
Err(e) => {
eprintln!("❌ Failed to prepare launch plan: {}", e);
}
}
}
Some(Commands::Update) => {
println!("🔄 Checking Roblox CDN for client updates...");
@ -62,6 +99,18 @@ pub fn run(args: &[String]) {
}
Some(Commands::Clean) => {
println!("🧹 Cleaning old versions and temporary cache...");
let cleaner = Cleaner::new(versions_dir, downloads_dir);
match cleaner.clean_stale_data("active") {
Ok(report) => {
println!("✅ Cleaning complete:");
println!(" • Purged old version directories: {}", report.purged_versions.len());
println!(" • Purged leftover download fragments: {}", report.purged_parts_count);
println!(" • Total disk space reclaimed: {:.2} MB", (report.reclaimed_bytes as f64) / (1024.0 * 1024.0));
}
Err(e) => {
eprintln!("❌ Cleaning failed: {}", e);
}
}
}
Some(Commands::Diagnostics) => {
println!("🩺 System diagnostics: checking bubblewrap sandbox status...");

View file

@ -8,6 +8,12 @@ fn test_cli_parse_launch_place() {
assert!(parsed.is_ok());
}
#[test]
fn test_cli_parse_region_flag() {
let parsed = Cli::try_parse_from(["molotov", "launch", "--region", "eu"]);
assert!(parsed.is_ok());
}
#[test]
fn test_cli_parse_clean_command() {
let parsed = Cli::try_parse_from(["molotov", "clean"]);

View file

@ -1,5 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-or-later
use eframe::egui;
use molotov_bootstrap::cleaner::Cleaner;
use molotov_bootstrap::launcher::{LaunchOptions, Launcher};
use molotov_bootstrap::region::ServerRegion;
use std::path::PathBuf;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Tab {
@ -16,6 +20,9 @@ pub struct MolotovApp {
pub safe_mode: bool,
pub fps_cap: i64,
pub vram_skip_mips: bool,
pub region: ServerRegion,
pub discord_rpc: bool,
pub status_message: String,
}
impl Default for MolotovApp {
@ -25,6 +32,9 @@ impl Default for MolotovApp {
safe_mode: false,
fps_cap: 144,
vram_skip_mips: false,
region: ServerRegion::Auto,
discord_rpc: true,
status_message: "Ready to launch".to_string(),
}
}
}
@ -40,6 +50,14 @@ impl MolotovApp {
Tab::Maintenance => "Maintenance",
}
}
fn get_paths() -> (PathBuf, PathBuf, PathBuf) {
let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
let base_dir = PathBuf::from(&home).join(".local/share/molotov");
let downloads_dir = PathBuf::from(&home).join(".cache/molotov/downloads");
let versions_dir = base_dir.join("versions");
(base_dir, downloads_dir, versions_dir)
}
}
impl eframe::App for MolotovApp {
@ -58,9 +76,44 @@ impl eframe::App for MolotovApp {
egui::CentralPanel::default().show(ctx, |ui| match self.current_tab {
Tab::Play => {
ui.heading("Molotov Roblox Runtime");
ui.label(format!("Status: {}", self.status_message));
ui.separator();
ui.checkbox(&mut self.safe_mode, "Safe Mode (Minimal Flags)");
ui.horizontal(|ui| {
ui.label("Preferred Server Region:");
egui::ComboBox::from_id_salt("region_combo")
.selected_text(self.region.display_name())
.show_ui(ui, |ui| {
ui.selectable_value(&mut self.region, ServerRegion::Auto, "Auto / Default");
ui.selectable_value(&mut self.region, ServerRegion::USEast, "US East (Virginia)");
ui.selectable_value(&mut self.region, ServerRegion::USWest, "US West (California)");
ui.selectable_value(&mut self.region, ServerRegion::Europe, "Europe (Frankfurt / London)");
ui.selectable_value(&mut self.region, ServerRegion::Asia, "Asia (Tokyo / Singapore)");
ui.selectable_value(&mut self.region, ServerRegion::Australia, "Australia (Sydney)");
});
});
ui.add_space(10.0);
if ui.button("🚀 Launch Roblox").clicked() {
println!("Launch clicked!");
let (base_dir, _, _) = Self::get_paths();
let launcher = Launcher::new(base_dir);
let options = LaunchOptions {
place_id: None,
safe_mode: self.safe_mode,
region: self.region,
instance_id: "default".to_string(),
};
match launcher.prepare_launch(options) {
Ok(plan) => {
self.status_message = format!("Launch plan prepared! Safe Mode: {}", plan.is_safe_mode);
}
Err(e) => {
self.status_message = format!("Launch preparation failed: {}", e);
}
}
}
}
Tab::FastFlags => {
@ -82,8 +135,21 @@ impl eframe::App for MolotovApp {
}
Tab::Maintenance => {
ui.heading("Maintenance & Cleaning");
ui.checkbox(&mut self.discord_rpc, "Enable Discord Rich Presence (RPC)");
ui.add_space(10.0);
if ui.button("Clean Stale Versions").clicked() {
println!("Clean clicked!");
let (_, downloads_dir, versions_dir) = Self::get_paths();
let cleaner = Cleaner::new(versions_dir, downloads_dir);
match cleaner.clean_stale_data("active") {
Ok(report) => {
let mb = (report.reclaimed_bytes as f64) / (1024.0 * 1024.0);
self.status_message = format!("Cleaned {} old versions, reclaimed {:.2} MB", report.purged_versions.len(), mb);
}
Err(e) => {
self.status_message = format!("Clean failed: {}", e);
}
}
}
}
});