Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixes #449

Merged
merged 10 commits into from
Aug 5, 2023
Merged

fixes #449

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion theseus/src/api/pack/import/atlauncher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,13 @@ async fn import_atlauncher_unmanaged(
.await?;

// Moves .minecraft folder over (ie: overrides such as resourcepacks, mods, etc)
copy_dotminecraft(profile_path.clone(), minecraft_folder).await?;
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?
Expand Down
54 changes: 47 additions & 7 deletions theseus/src/api/pack/import/curseforge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ use serde::{Deserialize, Serialize};
use crate::{
prelude::{ModLoader, ProfilePathId},
state::ProfileInstallStage,
util::io,
util::{
fetch::{fetch, write_cached_icon},
io,
},
State,
};

use super::copy_dotminecraft;
use super::{copy_dotminecraft, recache_icon};

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
Expand All @@ -31,12 +34,20 @@ pub struct FlameModLoader {
pub primary: bool,
}

#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct MinecraftInstance {
pub name: Option<String>,
pub profile_image_path: Option<PathBuf>,
pub installed_modpack: Option<InstalledModpack>,
pub game_version: String, // Minecraft game version. Non-prioritized, use this if Vanilla
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]

pub struct InstalledModpack {
pub thumbnail_url: Option<String>,
}

// Check if folder has a minecraftinstance.json that parses
pub async fn is_valid_curseforge(instance_folder: PathBuf) -> bool {
Expand All @@ -53,9 +64,6 @@ pub async fn import_curseforge(
curseforge_instance_folder: PathBuf, // instance's folder
profile_path: ProfilePathId, // path to profile
) -> crate::Result<()> {
// TODO: recache curseforge instance icon
let icon: Option<PathBuf> = None;

// Load minecraftinstance.json
let minecraft_instance: String = io::read_to_string(
&curseforge_instance_folder.join("minecraftinstance.json"),
Expand All @@ -72,6 +80,32 @@ pub async fn import_curseforge(
.unwrap_or("Unknown".to_string())
);

let state = State::get().await?;
// Recache Curseforge Icon if it exists
let mut icon = None;

if let Some(icon_path) = minecraft_instance.profile_image_path.clone() {
icon = recache_icon(icon_path).await?;
} else if let Some(InstalledModpack {
thumbnail_url: Some(thumbnail_url),
}) = minecraft_instance.installed_modpack.clone()
{
let icon_bytes =
fetch(&thumbnail_url, None, &state.fetch_semaphore).await?;
let filename = thumbnail_url.rsplit('/').last();
if let Some(filename) = filename {
icon = Some(
write_cached_icon(
filename,
&state.directories.caches_dir(),
icon_bytes,
&state.io_semaphore,
)
.await?,
);
}
}

// Curseforge vanilla profile may not have a manifest.json, so we allow it to not exist
if curseforge_instance_folder.join("manifest.json").exists() {
// Load manifest.json
Expand Down Expand Up @@ -146,7 +180,13 @@ pub async fn import_curseforge(
}

// Copy in contained folders as overrides
copy_dotminecraft(profile_path.clone(), curseforge_instance_folder).await?;
let state = State::get().await?;
copy_dotminecraft(
profile_path.clone(),
curseforge_instance_folder,
&state.io_semaphore,
)
.await?;

if let Some(profile_val) =
crate::api::profile::get(&profile_path, None).await?
Expand Down
8 changes: 7 additions & 1 deletion theseus/src/api/pack/import/gdlauncher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,13 @@ pub async fn import_gdlauncher(
.await?;

// Copy in contained folders as overrides
copy_dotminecraft(profile_path.clone(), gdlauncher_instance_folder).await?;
let state = State::get().await?;
copy_dotminecraft(
profile_path.clone(),
gdlauncher_instance_folder,
&state.io_semaphore,
)
.await?;

if let Some(profile_val) =
crate::api::profile::get(&profile_path, None).await?
Expand Down
8 changes: 7 additions & 1 deletion theseus/src/api/pack/import/mmc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,13 @@ async fn import_mmc_unmanaged(
.await?;

// Moves .minecraft folder over (ie: overrides such as resourcepacks, mods, etc)
copy_dotminecraft(profile_path.clone(), minecraft_folder).await?;
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?
Expand Down
50 changes: 40 additions & 10 deletions theseus/src/api/pack/import/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
use std::path::{Path, PathBuf};
use std::{
fmt,
path::{Path, PathBuf},
};

use io::IOError;
use serde::{Deserialize, Serialize};

use crate::{
prelude::ProfilePathId,
state::Profiles,
util::{fetch, io},
util::{
fetch::{self, IoSemaphore},
io,
},
};

pub mod atlauncher;
Expand All @@ -24,28 +30,46 @@ pub enum ImportLauncherType {
#[serde(other)]
Unknown,
}
// impl display
impl fmt::Display for ImportLauncherType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ImportLauncherType::MultiMC => write!(f, "MultiMC"),
ImportLauncherType::PrismLauncher => write!(f, "PrismLauncher"),
ImportLauncherType::ATLauncher => write!(f, "ATLauncher"),
ImportLauncherType::GDLauncher => write!(f, "GDLauncher"),
ImportLauncherType::Curseforge => write!(f, "Curseforge"),
ImportLauncherType::Unknown => write!(f, "Unknown"),
}
}
}

// Return a list of importable instances from a launcher type and base path, by iterating through the folder and checking
pub async fn get_importable_instances(
launcher_type: ImportLauncherType,
base_path: PathBuf,
) -> crate::Result<Vec<String>> {
// Some launchers have a different folder structure for instances
let instances_folder = match launcher_type {
let instances_subfolder = match launcher_type {
ImportLauncherType::GDLauncher
| ImportLauncherType::MultiMC
| ImportLauncherType::PrismLauncher
| ImportLauncherType::ATLauncher => base_path.join("instances"),
ImportLauncherType::Curseforge => base_path.join("Instances"),
| ImportLauncherType::ATLauncher => "instances",
ImportLauncherType::Curseforge => "Instances",
ImportLauncherType::Unknown => {
return Err(crate::ErrorKind::InputError(
"Launcher type Unknown".to_string(),
)
.into())
}
};
let instances_folder = base_path.join(instances_subfolder);
let mut instances = Vec::new();
let mut dir = io::read_dir(&instances_folder).await?;
let mut dir = io::read_dir(&instances_folder).await.map_err(| _ | {
crate::ErrorKind::InputError(format!(
"Invalid {launcher_type} launcher path, could not find '{instances_subfolder}' subfolder."
))
})?;
while let Some(entry) = dir
.next_entry()
.await
Expand Down Expand Up @@ -216,6 +240,7 @@ pub async fn recache_icon(
async fn copy_dotminecraft(
profile_path: ProfilePathId,
dotminecraft: PathBuf,
io_semaphore: &IoSemaphore,
) -> crate::Result<()> {
// Get full path to profile
let profile_path = profile_path.get_full_path().await?;
Expand All @@ -236,6 +261,7 @@ async fn copy_dotminecraft(
&path.display()
))
})?),
io_semaphore,
)
.await?;
}
Expand All @@ -247,9 +273,13 @@ async fn copy_dotminecraft(
#[theseus_macros::debug_pin]
#[async_recursion::async_recursion]
#[tracing::instrument]
async fn copy_dir_to(src: &Path, dst: &Path) -> crate::Result<()> {
async fn copy_dir_to(
src: &Path,
dst: &Path,
io_semaphore: &IoSemaphore,
) -> crate::Result<()> {
if !src.is_dir() {
io::copy(src, dst).await?;
fetch::copy(src, dst, io_semaphore).await?;
return Ok(());
}

Expand All @@ -273,10 +303,10 @@ async fn copy_dir_to(src: &Path, dst: &Path) -> crate::Result<()> {

if src_child.is_dir() {
// Recurse into sub-directory
copy_dir_to(&src_child, &dst_child).await?;
copy_dir_to(&src_child, &dst_child, io_semaphore).await?;
} else {
// Copy file
io::copy(&src_child, &dst_child).await?;
fetch::copy(&src_child, &dst_child, io_semaphore).await?;
}
}

Expand Down
13 changes: 11 additions & 2 deletions theseus/src/api/pack/install_mrpack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::prelude::ProfilePathId;
use crate::state::{ProfileInstallStage, Profiles, SideType};
use crate::util::fetch::{fetch_mirrors, write};
use crate::util::io;
use crate::State;
use crate::{profile, State};
use async_zip::tokio::read::seek::ZipFileReader;

use std::io::Cursor;
Expand Down Expand Up @@ -82,6 +82,7 @@ pub async fn install_zipped_mrpack_files(
let version_id = create_pack.description.version_id;
let existing_loading_bar = create_pack.description.existing_loading_bar;
let profile_path = create_pack.description.profile_path;
let icon_exists = icon.is_some();

let reader: Cursor<&bytes::Bytes> = Cursor::new(&file);

Expand Down Expand Up @@ -186,7 +187,7 @@ pub async fn install_zipped_mrpack_files(
let path = profile_path
.get_full_path()
.await?
.join(project.path);
.join(&project.path);
write(&path, &file, &state.io_semaphore)
.await?;
}
Expand Down Expand Up @@ -261,6 +262,14 @@ pub async fn install_zipped_mrpack_files(
}
}

// If the icon doesn't exist, we expect icon.png to be a potential icon.
// If it doesn't exist, and an override to icon.png exists, cache and use that
let potential_icon =
profile_path.get_full_path().await?.join("icon.png");
if !icon_exists && potential_icon.exists() {
profile::edit_icon(&profile_path, Some(&potential_icon)).await?;
}

if let Some(profile_val) =
crate::api::profile::get(&profile_path, None).await?
{
Expand Down
2 changes: 1 addition & 1 deletion theseus/src/api/profile/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ pub async fn profile_create(

emit_profile(
uuid,
profile.get_profile_full_path().await?,
&profile.profile_id(),
&profile.metadata.name,
ProfilePayloadType::Created,
)
Expand Down
Loading
Loading