278 lines
7.5 KiB
Rust
278 lines
7.5 KiB
Rust
use std::{collections::HashMap, path::PathBuf};
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::{
|
|
State,
|
|
install::{InstallPhaseDetails, InstallProgressReporter},
|
|
pack::{
|
|
self,
|
|
import::{self, finish_import},
|
|
install_from::CreatePackDescription,
|
|
},
|
|
prelude::ModLoader,
|
|
state::{
|
|
AppliedContentSetPatch, EditInstance, InstanceInstallStage,
|
|
InstanceLink,
|
|
},
|
|
util::io,
|
|
};
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ATInstance {
|
|
pub id: String, // minecraft version id ie: 1.12.1, not a name
|
|
pub launcher: ATLauncher,
|
|
pub java_version: ATJavaVersion,
|
|
}
|
|
#[derive(Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ATLauncher {
|
|
pub name: String,
|
|
pub pack: String,
|
|
pub version: String, // ie: 1.6
|
|
pub loader_version: ATLauncherLoaderVersion,
|
|
|
|
pub modrinth_project: Option<ATLauncherModrinthProject>,
|
|
pub modrinth_version: Option<ATLauncherModrinthVersion>,
|
|
pub modrinth_manifest: Option<pack::install_from::PackFormat>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ATJavaVersion {
|
|
pub major_version: u8,
|
|
pub component: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ATLauncherLoaderVersion {
|
|
pub r#type: String,
|
|
pub version: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
pub struct ATLauncherModrinthProject {
|
|
pub id: String,
|
|
pub slug: String,
|
|
pub project_type: String,
|
|
pub team: String,
|
|
pub client_side: Option<String>,
|
|
pub server_side: Option<String>,
|
|
pub categories: Vec<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
pub struct ATLauncherModrinthVersion {
|
|
pub id: String,
|
|
pub project_id: String,
|
|
pub name: String,
|
|
pub version_number: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ATLauncherModrinthVersionFile {
|
|
pub hashes: HashMap<String, String>,
|
|
pub url: String,
|
|
pub filename: String,
|
|
pub primary: bool,
|
|
pub size: u64,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ATLauncherModrinthVersionDependency {
|
|
pub project_id: Option<String>,
|
|
pub version_id: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ATLauncherMod {
|
|
pub name: String,
|
|
pub version: String,
|
|
pub file: String,
|
|
|
|
pub modrinth_project: Option<ATLauncherModrinthProject>,
|
|
pub modrinth_version: Option<ATLauncherModrinthVersion>,
|
|
}
|
|
|
|
// Check if folder has a instance.json that parses
|
|
pub async fn is_valid_atlauncher(instance_folder: PathBuf) -> bool {
|
|
let instance = serde_json::from_str::<ATInstance>(
|
|
&io::read_any_encoding_to_string(
|
|
&instance_folder.join("instance.json"),
|
|
)
|
|
.await
|
|
.unwrap_or(("".into(), encoding_rs::UTF_8))
|
|
.0,
|
|
);
|
|
|
|
if let Err(e) = instance {
|
|
tracing::warn!(
|
|
"Could not parse instance.json at {}: {}",
|
|
instance_folder.display(),
|
|
e
|
|
);
|
|
false
|
|
} else {
|
|
true
|
|
}
|
|
}
|
|
|
|
#[tracing::instrument]
|
|
|
|
pub async fn import_atlauncher_dir(
|
|
atlauncher_base_path: PathBuf,
|
|
atlauncher_instance_path: PathBuf,
|
|
instance_id: &str,
|
|
reporter: InstallProgressReporter,
|
|
details: InstallPhaseDetails,
|
|
symlink: bool,
|
|
) -> crate::Result<()> {
|
|
let atinstance = serde_json::from_str::<ATInstance>(
|
|
&io::read_any_encoding_to_string(
|
|
&atlauncher_instance_path.join("instance.json"),
|
|
)
|
|
.await
|
|
.unwrap_or(("".into(), encoding_rs::UTF_8))
|
|
.0,
|
|
)?;
|
|
|
|
let icon_path_primary = atlauncher_instance_path.join("instance.png");
|
|
let safe_pack_name = atinstance
|
|
.launcher
|
|
.pack
|
|
.replace(|c: char| !c.is_alphanumeric(), "")
|
|
.to_lowercase();
|
|
let icon_path_secondary = atlauncher_base_path
|
|
.join("configs")
|
|
.join("images")
|
|
.join(safe_pack_name + ".png");
|
|
let icon = match (icon_path_primary.exists(), icon_path_secondary.exists())
|
|
{
|
|
(true, _) => import::recache_icon(icon_path_primary).await?,
|
|
(_, true) => import::recache_icon(icon_path_secondary).await?,
|
|
_ => None,
|
|
};
|
|
|
|
let description = CreatePackDescription {
|
|
icon,
|
|
override_title: Some(atinstance.launcher.name.clone()),
|
|
project_id: None,
|
|
version_id: None,
|
|
instance_id: instance_id.to_string(),
|
|
source_filename: None,
|
|
};
|
|
|
|
let backup_name = format!(
|
|
"ATLauncher-{}",
|
|
atlauncher_instance_path
|
|
.file_name()
|
|
.map(|name| name.to_string_lossy())
|
|
.unwrap_or_default()
|
|
);
|
|
let minecraft_folder = atlauncher_instance_path;
|
|
|
|
import_atlauncher_unmanaged(
|
|
instance_id,
|
|
minecraft_folder,
|
|
backup_name,
|
|
description,
|
|
atinstance,
|
|
reporter,
|
|
details,
|
|
symlink,
|
|
)
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
|
|
async fn import_atlauncher_unmanaged(
|
|
instance_id: &str,
|
|
minecraft_folder: PathBuf,
|
|
backup_name: String,
|
|
description: CreatePackDescription,
|
|
atinstance: ATInstance,
|
|
reporter: InstallProgressReporter,
|
|
details: InstallPhaseDetails,
|
|
symlink: bool,
|
|
) -> crate::Result<()> {
|
|
let mod_loader = format!(
|
|
"\"{}\"",
|
|
atinstance.launcher.loader_version.r#type.to_lowercase()
|
|
);
|
|
let mod_loader: ModLoader = serde_json::from_str::<ModLoader>(&mod_loader)
|
|
.map_err(|_| {
|
|
crate::ErrorKind::InputError(format!(
|
|
"Could not parse mod loader type: {mod_loader}"
|
|
))
|
|
})?;
|
|
|
|
let game_version = atinstance.id;
|
|
|
|
let loader_version = if mod_loader != ModLoader::Vanilla {
|
|
crate::launcher::get_loader_version_from_profile(
|
|
&game_version,
|
|
mod_loader,
|
|
Some(&atinstance.launcher.loader_version.version),
|
|
)
|
|
.await?
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let link = match (&description.project_id, &description.version_id) {
|
|
(Some(project_id), Some(version_id)) => {
|
|
Some(InstanceLink::ModrinthModpack {
|
|
project_id: project_id.clone(),
|
|
version_id: version_id.clone(),
|
|
})
|
|
}
|
|
_ => None,
|
|
};
|
|
crate::api::instance::edit(
|
|
instance_id,
|
|
EditInstance {
|
|
install_stage: Some(InstanceInstallStage::PackInstalling),
|
|
name: Some(
|
|
description
|
|
.override_title
|
|
.clone()
|
|
.unwrap_or_else(|| backup_name.to_string()),
|
|
),
|
|
icon_path: Some(
|
|
description
|
|
.icon
|
|
.clone()
|
|
.map(|x| x.to_string_lossy().to_string()),
|
|
),
|
|
link,
|
|
content_set_patch: Some(AppliedContentSetPatch {
|
|
source_kind: None,
|
|
game_version: Some(game_version.clone()),
|
|
protocol_version: Some(None),
|
|
loader: Some(mod_loader),
|
|
loader_version: Some(loader_version.clone().map(|x| x.id)),
|
|
}),
|
|
..EditInstance::default()
|
|
},
|
|
)
|
|
.await?;
|
|
|
|
// Moves .minecraft folder over (ie: overrides such as resourcepacks, mods, etc)
|
|
let state = State::get().await?;
|
|
finish_import(
|
|
instance_id,
|
|
minecraft_folder,
|
|
&state.io_semaphore,
|
|
reporter,
|
|
details,
|
|
symlink,
|
|
)
|
|
.await?;
|
|
Ok(())
|
|
}
|