You've already forked AstralRinth
forked from xxxOFFxxx/AstralRinth
* fixes * prettier * more bugs * changes * more fixes * prettier, fmt, clippy * fix regressed error * println, console.log * fix imports --------- Co-authored-by: Geometrically <18202329+Geometrically@users.noreply.github.com> Co-authored-by: Jai A <jaiagr+gpg@pm.me>
250 lines
7.3 KiB
Rust
250 lines
7.3 KiB
Rust
use std::{collections::HashMap, path::PathBuf};
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::{
|
|
event::LoadingBarId,
|
|
pack::{
|
|
self,
|
|
import::{self, copy_dotminecraft},
|
|
install_from::CreatePackDescription,
|
|
},
|
|
prelude::{ModLoader, ProfilePathId},
|
|
state::{LinkedData, ProfileInstallStage},
|
|
util::io,
|
|
State,
|
|
};
|
|
|
|
#[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>,
|
|
|
|
pub mods: Vec<ATLauncherMod>,
|
|
}
|
|
|
|
#[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 title: String,
|
|
pub description: String,
|
|
pub body: String,
|
|
pub client_side: Option<String>,
|
|
pub server_side: Option<String>,
|
|
pub categories: Vec<String>,
|
|
pub icon_url: 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: String =
|
|
io::read_to_string(&instance_folder.join("instance.json"))
|
|
.await
|
|
.unwrap_or("".to_string());
|
|
let instance: Result<ATInstance, serde_json::Error> =
|
|
serde_json::from_str::<ATInstance>(&instance);
|
|
instance.is_ok()
|
|
}
|
|
|
|
#[tracing::instrument]
|
|
#[theseus_macros::debug_pin]
|
|
pub async fn import_atlauncher(
|
|
atlauncher_base_path: PathBuf, // path to base atlauncher folder
|
|
instance_folder: String, // instance folder in atlauncher_base_path
|
|
profile_path: ProfilePathId, // path to profile
|
|
) -> crate::Result<()> {
|
|
let atlauncher_instance_path = atlauncher_base_path
|
|
.join("instances")
|
|
.join(instance_folder.clone());
|
|
|
|
// Load instance.json
|
|
let atinstance: String =
|
|
io::read_to_string(&atlauncher_instance_path.join("instance.json"))
|
|
.await?;
|
|
let atinstance: ATInstance =
|
|
serde_json::from_str::<ATInstance>(&atinstance)?;
|
|
|
|
// Icon path should be {instance_folder}/instance.png if it exists,
|
|
// Second possibility is ATLauncher/configs/images/{safe_pack_name}.png (safe pack name is alphanumeric lowercase)
|
|
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,
|
|
};
|
|
|
|
// Create description from instance.cfg
|
|
let description = CreatePackDescription {
|
|
icon,
|
|
override_title: Some(atinstance.launcher.name.clone()),
|
|
project_id: None,
|
|
version_id: None,
|
|
existing_loading_bar: None,
|
|
profile_path: profile_path.clone(),
|
|
};
|
|
|
|
let backup_name = format!("ATLauncher-{}", instance_folder);
|
|
let minecraft_folder = atlauncher_instance_path;
|
|
|
|
import_atlauncher_unmanaged(
|
|
profile_path,
|
|
minecraft_folder,
|
|
backup_name,
|
|
description,
|
|
atinstance,
|
|
None,
|
|
)
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
|
|
async fn import_atlauncher_unmanaged(
|
|
profile_path: ProfilePathId,
|
|
minecraft_folder: PathBuf,
|
|
backup_name: String,
|
|
description: CreatePackDescription,
|
|
atinstance: ATInstance,
|
|
existing_loading_bar: Option<LoadingBarId>,
|
|
) -> 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::profile::create::get_loader_version_from_loader(
|
|
game_version.clone(),
|
|
mod_loader,
|
|
Some(atinstance.launcher.loader_version.version.clone()),
|
|
)
|
|
.await?
|
|
} else {
|
|
None
|
|
};
|
|
|
|
// Set profile data to created default profile
|
|
crate::api::profile::edit(&profile_path, |prof| {
|
|
prof.metadata.name = description
|
|
.override_title
|
|
.clone()
|
|
.unwrap_or_else(|| backup_name.to_string());
|
|
prof.install_stage = ProfileInstallStage::PackInstalling;
|
|
prof.metadata.linked_data = Some(LinkedData {
|
|
project_id: description.project_id.clone(),
|
|
version_id: description.version_id.clone(),
|
|
});
|
|
prof.metadata.icon = description.icon.clone();
|
|
prof.metadata.game_version = game_version.clone();
|
|
prof.metadata.loader_version = loader_version.clone();
|
|
prof.metadata.loader = mod_loader;
|
|
|
|
async { Ok(()) }
|
|
})
|
|
.await?;
|
|
|
|
// Moves .minecraft folder over (ie: overrides such as resourcepacks, mods, etc)
|
|
let state = State::get().await?;
|
|
copy_dotminecraft(
|
|
profile_path.clone(),
|
|
minecraft_folder,
|
|
&state.io_semaphore,
|
|
)
|
|
.await?;
|
|
|
|
if let Some(profile_val) =
|
|
crate::api::profile::get(&profile_path, None).await?
|
|
{
|
|
crate::launcher::install_minecraft(&profile_val, existing_loading_bar)
|
|
.await?;
|
|
|
|
State::sync().await?;
|
|
}
|
|
Ok(())
|
|
}
|