late-java-core 2.2.9

A Rust library for launching Minecraft Java Edition
use late_java_core::{
    Launch, MicrosoftAuth, AZauthAuth, LaunchOptions, MemoryOptions, LoaderOptions, 
    ScreenOptions, JavaOptions, VersionManifest, StatusClient, AZauthUser, init_logger
};
use std::collections::HashMap;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    init_logger();

    println!("=== Late Java Core - Ejemplo Completo con AZauth ===");
    
    // 1. Mostrar versiones disponibles
    println!("\n1. Obteniendo versiones disponibles...");
    let manifest = VersionManifest::fetch().await?;
    println!("   Última release: {}", manifest.latest.release);
    println!("   Última snapshot: {}", manifest.latest.snapshot);

    // 2. Autenticación AZauth con soporte para 2FA
    println!("\n2. Autenticación AZauth...");
    let azauth = AZauthAuth::new("https://nincraft.fr".to_string());
    
    // Simular login con 2FA
    println!("   Intentando login con AZauth...");
    let azauth_result = azauth.login("usuario@ejemplo.com", "contraseña", None).await?;
    
    if let Some(true) = azauth_result.a2f {
        println!("   2FA requerido! Ingresando código...");
        // En un ejemplo real, el usuario ingresaría el código 2FA
        let azauth_result = azauth.login("usuario@ejemplo.com", "contraseña", Some("123456")).await?;
    }
    
    if let Some(true) = azauth_result.error {
        println!("   Error en autenticación: {:?}", azauth_result.reason);
        return Ok(());
    }
    
    println!("   Autenticado exitosamente como: {:?}", azauth_result.name);
    
    // Mostrar información del usuario
    if let Some(user_info) = &azauth_result.user_info {
        println!("   ID: {:?}", user_info.id);
        println!("   Rol: {:?}", user_info.role);
        println!("   Verificado: {:?}", user_info.verified);
        println!("   Dinero: {:?}", user_info.money);
    }
    
    // Mostrar skins
    if let Some(profile) = &azauth_result.profile {
        println!("   Skins disponibles: {}", profile.skins.len());
        for (i, skin) in profile.skins.iter().enumerate() {
            println!("     Skin {}: URL = {:?}, Base64 = {}", 
                i + 1, 
                skin.url, 
                if skin.base64.is_some() { "" } else { "No" }
            );
        }
    }

    // 3. Verificar sesión
    println!("\n3. Verificando sesión...");
    let verified_user = azauth.verify(&azauth_result).await?;
    if let Some(true) = verified_user.error {
        println!("   Error al verificar sesión: {:?}", verified_user.reason);
    } else {
        println!("   Sesión verificada exitosamente");
    }

    // 4. Autenticación Microsoft (para comparación)
    println!("\n4. Autenticación Microsoft...");
    let microsoft_auth = MicrosoftAuth::new("00000000402b5328".to_string());
    
    // En un ejemplo real, descomenta la siguiente línea:
    // let microsoft_result = microsoft_auth.authenticate().await?;
    // println!("   Microsoft: {}", microsoft_result.name);
    
    // Para el ejemplo, creamos un resultado simulado
    let microsoft_result = late_java_core::AuthResponse {
        access_token: "simulated_token".to_string(),
        client_token: "simulated_client_token".to_string(),
        uuid: "simulated_uuid".to_string(),
        name: "TestUser".to_string(),
        refresh_token: Some("simulated_refresh_token".to_string()),
        user_properties: "{}".to_string(),
        meta: late_java_core::auth::AuthMeta {
            auth_type: "Xbox".to_string(),
            access_token_expires_in: chrono::Utc::now().timestamp() as u64 + 3600,
            demo: false,
        },
        xbox_account: Some(late_java_core::auth::XboxAccount {
            xuid: "simulated_xuid".to_string(),
            gamertag: "TestGamer".to_string(),
            age_group: "Adult".to_string(),
        }),
        profile: None,
    };

    // 5. Ping a servidor
    println!("\n5. Ping a servidor...");
    match StatusClient::ping("mc.hypixel.net", 25565) {
        Ok(result) => {
            println!("   Servidor: {}", result.server_info.description.text);
            println!("   Jugadores: {}/{}", result.server_info.players.online, result.server_info.players.max);
            println!("   Latencia: {}ms", result.latency);
        }
        Err(e) => {
            println!("   Error al hacer ping: {}", e);
        }
    }

    // 6. Configuración del launcher con AZauth
    println!("\n6. Configurando launcher con AZauth...");
    
    let mut custom_headers = HashMap::new();
    custom_headers.insert("User-Agent".to_string(), "LateJavaCore/2.2.9".to_string());
    
    // Convertir AZauthUser a AuthResponse para el launcher
    let auth_response = late_java_core::AuthResponse {
        access_token: azauth_result.access_token.unwrap_or_default(),
        client_token: azauth_result.client_token.unwrap_or_default(),
        uuid: azauth_result.uuid.unwrap_or_default(),
        name: azauth_result.name.unwrap_or_default(),
        refresh_token: None,
        user_properties: azauth_result.user_properties.unwrap_or_default(),
        meta: late_java_core::auth::AuthMeta {
            auth_type: "AZauth".to_string(),
            access_token_expires_in: chrono::Utc::now().timestamp() as u64 + 86400,
            demo: false,
        },
        xbox_account: None,
        profile: azauth_result.profile.map(|p| late_java_core::auth::MinecraftProfile {
            skins: p.skins,
            capes: vec![],
        }),
    };
    
    let options = LaunchOptions {
        // Configuración básica
        path: "./minecraft".to_string(),
        version: "1.20.4".to_string(),
        authenticator: auth_response,
        
        // Configuración de red
        url: Some("https://launchermeta.mojang.com".to_string()),
        headers: Some(custom_headers),
        timeout: Some(30000),
        
        // Configuración de instancia
        instance: Some("azauth_modded".to_string()),
        detached: Some(false),
        download_file_multiple: Some(10),
        bypass_offline: Some(false),
        
        // Configuración de memoria
        memory: MemoryOptions {
            min: "4G".to_string(),
            max: "8G".to_string(),
        },
        
        // Configuración de pantalla
        screen: ScreenOptions {
            width: Some(1920),
            height: Some(1080),
            fullscreen: false,
        },
        
        // Configuración de Java
        java: JavaOptions {
            path: None,
            version: Some("17".to_string()),
            java_type: "jre".to_string(),
        },
        
        // Configuración del loader
        loader: LoaderOptions {
            path: Some("./loader".to_string()),
            loader_type: Some("fabric".to_string()),
            build: Some("0.15.9".to_string()),
            enable: true,
        },
        
        // Configuración MCP (opcional)
        mcp: None,
        
        // Verificación de archivos
        verify: Some(true),
        ignored: vec!["saves".to_string(), "screenshots".to_string()],
        
        // Argumentos personalizados
        jvm_args: vec![
            "-XX:+UseG1GC".to_string(),
            "-XX:+ParallelRefProcEnabled".to_string(),
            "-XX:MaxGCPauseMillis=200".to_string(),
        ],
        game_args: vec![
            "--quickPlaySingleplayer".to_string(),
            "test_world".to_string(),
        ],
    };
    
    println!("   Configuración completada:");
    println!("     - Ruta: {}", options.path);
    println!("     - Versión: {}", options.version);
    println!("     - Instancia: {:?}", options.instance);
    println!("     - Autenticador: AZauth");
    println!("     - Memoria: {} - {}", options.memory.min, options.memory.max);
    println!("     - Loader: {:?} {:?}", options.loader.loader_type, options.loader.build);

    // 7. Crear launcher y suscribirse a eventos
    println!("\n7. Creando launcher...");
    let mut launcher = Launch::new();
    let mut event_receiver = launcher.subscribe();
    
    // Escuchar eventos en un hilo separado
    let event_handle = tokio::spawn(async move {
        while let Ok(event) = event_receiver.recv().await {
            match event {
                late_java_core::LaunchEvent::Progress { downloaded, total, element } => {
                    let percentage = if total > 0 { (downloaded * 100) / total } else { 0 };
                    println!("   Progreso: {}% ({}/{} bytes) - {:?}", percentage, downloaded, total, element);
                }
                late_java_core::LaunchEvent::Speed(speed) => {
                    println!("   Velocidad: {:.2} KB/s", speed / 1024.0);
                }
                late_java_core::LaunchEvent::Estimated(time) => {
                    println!("   Tiempo restante: {:.0} segundos", time);
                }
                late_java_core::LaunchEvent::Extract(file) => {
                    println!("   Extrayendo: {}", file);
                }
                late_java_core::LaunchEvent::Data(data) => {
                    println!("   Minecraft: {}", data.trim());
                }
                late_java_core::LaunchEvent::Close(msg) => {
                    println!("   {}", msg);
                    break;
                }
                late_java_core::LaunchEvent::Error(err) => {
                    println!("   Error: {}", err);
                    break;
                }
                _ => {}
            }
        }
    });

    // 8. Lanzar Minecraft
    println!("\n8. Lanzando Minecraft...");
    println!("   (En un ejemplo real, esto lanzaría Minecraft)");
    
    // En un ejemplo real, descomenta la siguiente línea:
    // launcher.launch(options).await?;
    
    // Simular el lanzamiento
    tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
    println!("   Minecraft lanzado exitosamente!");
    
    // Esperar a que termine el evento handler
    event_handle.abort();

    // 9. Cerrar sesión AZauth
    println!("\n9. Cerrando sesión AZauth...");
    match azauth.signout(&azauth_result).await {
        Ok(true) => println!("   Sesión cerrada exitosamente"),
        Ok(false) => println!("   Error al cerrar sesión"),
        Err(e) => println!("   Error: {}", e),
    }

    // 10. Mostrar información adicional
    println!("\n10. Información adicional:");
    println!("   - Versión de la librería: {}", late_java_core::VERSION);
    println!("   - Plataforma: {} {}", std::env::consts::OS, std::env::consts::ARCH);
    println!("   - Rust: {}", env!("RUSTC_SEMVER"));
    
    println!("\n=== Ejemplo completado ===");
    Ok(())
}