use crate::error::{Result, LateJavaCoreError};
use crate::minecraft_loader::{LoaderDownloader, LoaderType};
use std::path::Path;
#[derive(Debug, Clone)]
pub struct LoaderOptions {
pub path: String,
pub loader: LoaderConfig,
pub download_file_multiple: Option<u32>,
}
#[derive(Debug, Clone)]
pub struct LoaderConfig {
pub path: Option<String>,
pub loader_type: Option<String>,
pub build: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct LoaderJson {
pub libraries: Vec<LoaderLibrary>,
pub arguments: Option<LoaderArguments>,
pub main_class: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct LoaderLibrary {
pub loader: Option<String>,
pub name: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct LoaderArguments {
pub game: Option<Vec<String>>,
pub jvm: Option<Vec<String>>,
}
pub struct MinecraftLoader {
options: LoaderOptions,
loader_path: String,
}
impl MinecraftLoader {
pub fn new(options: LoaderOptions) -> Self {
let loader_path = Path::new(&options.path)
.join(options.loader.path.as_deref().unwrap_or("./loader"))
.to_string_lossy()
.to_string();
Self {
options,
loader_path,
}
}
pub async fn get_loader(&self, version: &str, java_path: &str) -> Result<LoaderJson> {
let loader_type = self.options.loader.loader_type.as_deref()
.ok_or_else(|| LateJavaCoreError::Minecraft("Loader type not specified".to_string()))?;
let loader_type_enum = match loader_type.to_lowercase().as_str() {
"forge" => LoaderType::Forge,
"neoforge" => LoaderType::NeoForge,
"fabric" => LoaderType::Fabric,
"legacyfabric" => LoaderType::LegacyFabric,
"quilt" => LoaderType::Quilt,
_ => return Err(LateJavaCoreError::Minecraft(format!("Unknown loader type: {}", loader_type))),
};
let loader = LoaderDownloader::new(crate::minecraft_loader::LoaderDownloaderOptions {
path: self.loader_path.clone(),
download_file_multiple: self.options.download_file_multiple,
loader: crate::minecraft_loader::LoaderConfig {
loader_type: loader_type_enum,
version: version.to_string(),
build: self.options.loader.build.clone(),
config: crate::minecraft_loader::LoaderConfigData {
java_path: java_path.to_string(),
minecraft_jar: format!("{}/versions/{}/{}.jar", self.options.path, version, version),
minecraft_json: format!("{}/versions/{}/{}.json", self.options.path, version, version),
},
},
});
let json = loader.install().await?;
let mut modified_json = json;
for lib in &mut modified_json.libraries {
lib.loader = Some(self.loader_path.clone());
}
Ok(modified_json)
}
pub async fn get_arguments(&self, json: Option<&LoaderJson>, version: &str) -> Result<LoaderArgumentsResult> {
if json.is_none() {
return Ok(LoaderArgumentsResult {
game: Vec::new(),
jvm: Vec::new(),
main_class: None,
});
}
let json = json.unwrap();
let modded_args = &json.arguments;
if modded_args.is_none() {
return Ok(LoaderArgumentsResult {
game: Vec::new(),
jvm: Vec::new(),
main_class: None,
});
}
let modded_args = modded_args.unwrap();
let mut args = LoaderArgumentsResult {
game: modded_args.game.clone().unwrap_or_default(),
jvm: Vec::new(),
main_class: json.main_class.clone(),
};
if let Some(jvm_args) = &modded_args.jvm {
args.jvm = jvm_args.iter()
.map(|jvm_arg| {
jvm_arg
.replace("${version_name}", version)
.replace("${library_directory}", &format!("{}/libraries", self.loader_path))
.replace("${classpath_separator}", if std::env::consts::OS == "windows" { ";" } else { ":" })
})
.collect();
}
Ok(args)
}
}
#[derive(Debug, Clone)]
pub struct LoaderArgumentsResult {
pub game: Vec<String>,
pub jvm: Vec<String>,
pub main_class: Option<String>,
}