added box pins (#110)

* added box pins

* clippy
This commit is contained in:
Wyatt Verchere
2023-05-09 12:05:52 -07:00
committed by GitHub
parent 1796c48fc7
commit da4fc1c835
6 changed files with 1146 additions and 1095 deletions

View File

@@ -79,97 +79,101 @@ pub async fn install_pack_from_version_id(
title: Option<String>,
) -> crate::Result<PathBuf> {
let state = State::get().await?;
Box::pin(async move {
let loading_bar = init_loading(
LoadingBarType::PackFileDownload {
pack_name: title,
pack_version: version_id.clone(),
},
100.0,
"Downloading pack file",
)
.await?;
let loading_bar = init_loading(
LoadingBarType::PackFileDownload {
pack_name: title,
pack_version: version_id.clone(),
},
100.0,
"Downloading pack file",
)
.await?;
emit_loading(&loading_bar, 0.0, Some("Fetching version")).await?;
let version: ModrinthVersion = fetch_json(
Method::GET,
&format!("{}version/{}", MODRINTH_API_URL, version_id),
None,
None,
&state.fetch_semaphore,
)
.await?;
emit_loading(&loading_bar, 10.0, None).await?;
emit_loading(&loading_bar, 0.0, Some("Fetching version")).await?;
let version: ModrinthVersion = fetch_json(
Method::GET,
&format!("{}version/{}", MODRINTH_API_URL, version_id),
None,
None,
&state.fetch_semaphore,
)
.await?;
emit_loading(&loading_bar, 10.0, None).await?;
let (url, hash) =
if let Some(file) = version.files.iter().find(|x| x.primary) {
Some((file.url.clone(), file.hashes.get("sha1")))
} else {
version
.files
.first()
.map(|file| (file.url.clone(), file.hashes.get("sha1")))
}
.ok_or_else(|| {
crate::ErrorKind::InputError(
"Specified version has no files".to_string(),
)
})?;
let file = fetch_advanced(
Method::GET,
&url,
hash.map(|x| &**x),
None,
None,
Some((&loading_bar, 70.0)),
&state.fetch_semaphore,
)
.await?;
emit_loading(&loading_bar, 0.0, Some("Fetching project metadata")).await?;
let project: ModrinthProject = fetch_json(
Method::GET,
&format!("{}project/{}", MODRINTH_API_URL, version.project_id),
None,
None,
&state.fetch_semaphore,
)
.await?;
emit_loading(&loading_bar, 10.0, Some("Retrieving icon")).await?;
let icon = if let Some(icon_url) = project.icon_url {
let state = State::get().await?;
let icon_bytes = fetch(&icon_url, None, &state.fetch_semaphore).await?;
let filename = icon_url.rsplit('/').next();
if let Some(filename) = filename {
Some(
write_cached_icon(
filename,
&state.directories.caches_dir(),
icon_bytes,
&state.io_semaphore,
let (url, hash) =
if let Some(file) = version.files.iter().find(|x| x.primary) {
Some((file.url.clone(), file.hashes.get("sha1")))
} else {
version
.files
.first()
.map(|file| (file.url.clone(), file.hashes.get("sha1")))
}
.ok_or_else(|| {
crate::ErrorKind::InputError(
"Specified version has no files".to_string(),
)
.await?,
)
})?;
let file = fetch_advanced(
Method::GET,
&url,
hash.map(|x| &**x),
None,
None,
Some((&loading_bar, 70.0)),
&state.fetch_semaphore,
)
.await?;
emit_loading(&loading_bar, 0.0, Some("Fetching project metadata"))
.await?;
let project: ModrinthProject = fetch_json(
Method::GET,
&format!("{}project/{}", MODRINTH_API_URL, version.project_id),
None,
None,
&state.fetch_semaphore,
)
.await?;
emit_loading(&loading_bar, 10.0, Some("Retrieving icon")).await?;
let icon = if let Some(icon_url) = project.icon_url {
let state = State::get().await?;
let icon_bytes =
fetch(&icon_url, None, &state.fetch_semaphore).await?;
let filename = icon_url.rsplit('/').next();
if let Some(filename) = filename {
Some(
write_cached_icon(
filename,
&state.directories.caches_dir(),
icon_bytes,
&state.io_semaphore,
)
.await?,
)
} else {
None
}
} else {
None
}
} else {
None
};
emit_loading(&loading_bar, 10.0, None).await?;
};
emit_loading(&loading_bar, 10.0, None).await?;
install_pack(
file,
icon,
Some(project.title),
Some(version.project_id),
Some(version.id),
Some(loading_bar),
)
install_pack(
file,
icon,
Some(project.title),
Some(version.project_id),
Some(version.id),
Some(loading_bar),
)
.await
})
.await
}
@@ -189,243 +193,259 @@ async fn install_pack(
) -> crate::Result<PathBuf> {
let state = &State::get().await?;
let reader = Cursor::new(&file);
Box::pin(async move {
let reader: Cursor<&bytes::Bytes> = Cursor::new(&file);
// Create zip reader around file
let mut zip_reader = ZipFileReader::new(reader).await.map_err(|_| {
crate::Error::from(crate::ErrorKind::InputError(
"Failed to read input modpack zip".to_string(),
))
})?;
// Create zip reader around file
let mut zip_reader =
ZipFileReader::new(reader).await.map_err(|_| {
crate::Error::from(crate::ErrorKind::InputError(
"Failed to read input modpack zip".to_string(),
))
})?;
// Extract index of modrinth.index.json
let zip_index_option = zip_reader
.file()
.entries()
.iter()
.position(|f| f.entry().filename() == "modrinth.index.json");
if let Some(zip_index) = zip_index_option {
let mut manifest = String::new();
let entry = zip_reader
// Extract index of modrinth.index.json
let zip_index_option = zip_reader
.file()
.entries()
.get(zip_index)
.unwrap()
.entry()
.clone();
let mut reader = zip_reader.entry(zip_index).await?;
reader.read_to_string_checked(&mut manifest, &entry).await?;
.iter()
.position(|f| f.entry().filename() == "modrinth.index.json");
if let Some(zip_index) = zip_index_option {
let mut manifest = String::new();
let entry = zip_reader
.file()
.entries()
.get(zip_index)
.unwrap()
.entry()
.clone();
let mut reader = zip_reader.entry(zip_index).await?;
reader.read_to_string_checked(&mut manifest, &entry).await?;
let pack: PackFormat = serde_json::from_str(&manifest)?;
let pack: PackFormat = serde_json::from_str(&manifest)?;
if &*pack.game != "minecraft" {
return Err(crate::ErrorKind::InputError(
"Pack does not support Minecraft".to_string(),
)
.into());
}
let mut game_version = None;
let mut mod_loader = None;
let mut loader_version = None;
for (key, value) in &pack.dependencies {
match key {
PackDependency::Forge => {
mod_loader = Some(ModLoader::Forge);
loader_version = Some(value);
}
PackDependency::FabricLoader => {
mod_loader = Some(ModLoader::Fabric);
loader_version = Some(value);
}
PackDependency::QuiltLoader => {
mod_loader = Some(ModLoader::Quilt);
loader_version = Some(value);
}
PackDependency::Minecraft => game_version = Some(value),
if &*pack.game != "minecraft" {
return Err(crate::ErrorKind::InputError(
"Pack does not support Minecraft".to_string(),
)
.into());
}
}
let game_version = if let Some(game_version) = game_version {
game_version
} else {
return Err(crate::ErrorKind::InputError(
"Pack did not specify Minecraft version".to_string(),
)
.into());
};
let profile_raw = crate::api::profile_create::profile_create(
override_title.unwrap_or_else(|| pack.name.clone()),
game_version.clone(),
mod_loader.unwrap_or(ModLoader::Vanilla),
loader_version.cloned(),
icon,
Some(LinkedData {
project_id: project_id.clone(),
version_id: version_id.clone(),
}),
Some(true),
)
.await?;
let profile = profile_raw.clone();
let result = async {
let loading_bar = init_or_edit_loading(
existing_loading_bar,
LoadingBarType::PackDownload {
pack_name: pack.name.clone(),
pack_id: project_id,
pack_version: version_id,
},
100.0,
"Downloading modpack",
)
.await?;
let num_files = pack.files.len();
use futures::StreamExt;
loading_try_for_each_concurrent(
futures::stream::iter(pack.files.into_iter())
.map(Ok::<PackFile, crate::Error>),
None,
Some(&loading_bar),
70.0,
num_files,
None,
|project| {
let profile = profile.clone();
async move {
//TODO: Future update: prompt user for optional files in a modpack
if let Some(env) = project.env {
if env
.get(&EnvType::Client)
.map(|x| x == &SideType::Unsupported)
.unwrap_or(false)
{
return Ok(());
}
}
let file = fetch_mirrors(
&project
.downloads
.iter()
.map(|x| &**x)
.collect::<Vec<&str>>(),
project
.hashes
.get(&PackFileHash::Sha1)
.map(|x| &**x),
&state.fetch_semaphore,
)
.await?;
let path = std::path::Path::new(&project.path)
.components()
.next();
if let Some(path) = path {
match path {
Component::CurDir | Component::Normal(_) => {
let path = profile.join(project.path);
write(&path, &file, &state.io_semaphore)
.await?;
}
_ => {}
};
}
Ok(())
let mut game_version = None;
let mut mod_loader = None;
let mut loader_version = None;
for (key, value) in &pack.dependencies {
match key {
PackDependency::Forge => {
mod_loader = Some(ModLoader::Forge);
loader_version = Some(value);
}
},
)
.await?;
let extract_overrides = |overrides: String| async {
let reader = Cursor::new(&file);
let mut overrides_zip =
ZipFileReader::new(reader).await.map_err(|_| {
crate::Error::from(crate::ErrorKind::InputError(
"Failed extract overrides Zip".to_string(),
))
})?;
let profile = profile.clone();
async move {
for index in 0..overrides_zip.file().entries().len() {
let file = overrides_zip
.file()
.entries()
.get(index)
.unwrap()
.entry()
.clone();
let file_path = PathBuf::from(file.filename());
if file.filename().starts_with(&overrides)
&& !file.filename().ends_with('/')
{
// Reads the file into the 'content' variable
let mut content = Vec::new();
let mut reader = overrides_zip.entry(index).await?;
reader
.read_to_end_checked(&mut content, &file)
.await?;
let mut new_path = PathBuf::new();
let components = file_path.components().skip(1);
for component in components {
new_path.push(component);
}
if new_path.file_name().is_some() {
write(
&profile.join(new_path),
&content,
&state.io_semaphore,
)
.await?;
}
}
PackDependency::FabricLoader => {
mod_loader = Some(ModLoader::Fabric);
loader_version = Some(value);
}
Ok::<(), crate::Error>(())
PackDependency::QuiltLoader => {
mod_loader = Some(ModLoader::Quilt);
loader_version = Some(value);
}
PackDependency::Minecraft => game_version = Some(value),
}
.await
}
let game_version = if let Some(game_version) = game_version {
game_version
} else {
return Err(crate::ErrorKind::InputError(
"Pack did not specify Minecraft version".to_string(),
)
.into());
};
emit_loading(&loading_bar, 0.0, Some("Extracting overrides"))
.await?;
extract_overrides("overrides".to_string()).await?;
extract_overrides("client_overrides".to_string()).await?;
emit_loading(&loading_bar, 29.9, Some("Done extacting overrides"))
let profile_raw = crate::api::profile_create::profile_create(
override_title.unwrap_or_else(|| pack.name.clone()),
game_version.clone(),
mod_loader.unwrap_or(ModLoader::Vanilla),
loader_version.cloned(),
icon,
Some(LinkedData {
project_id: project_id.clone(),
version_id: version_id.clone(),
}),
Some(true),
)
.await?;
let profile = profile_raw.clone();
let result = async {
let loading_bar = init_or_edit_loading(
existing_loading_bar,
LoadingBarType::PackDownload {
pack_name: pack.name.clone(),
pack_id: project_id,
pack_version: version_id,
},
100.0,
"Downloading modpack",
)
.await?;
if let Some(profile) = crate::api::profile::get(&profile).await? {
tokio::try_join!(
super::profile::sync(&profile.path),
crate::launcher::install_minecraft(
&profile,
Some(loading_bar)
),
)?;
let num_files = pack.files.len();
use futures::StreamExt;
loading_try_for_each_concurrent(
futures::stream::iter(pack.files.into_iter())
.map(Ok::<PackFile, crate::Error>),
None,
Some(&loading_bar),
70.0,
num_files,
None,
|project| {
let profile = profile.clone();
async move {
//TODO: Future update: prompt user for optional files in a modpack
if let Some(env) = project.env {
if env
.get(&EnvType::Client)
.map(|x| x == &SideType::Unsupported)
.unwrap_or(false)
{
return Ok(());
}
}
let file = fetch_mirrors(
&project
.downloads
.iter()
.map(|x| &**x)
.collect::<Vec<&str>>(),
project
.hashes
.get(&PackFileHash::Sha1)
.map(|x| &**x),
&state.fetch_semaphore,
)
.await?;
let path = std::path::Path::new(&project.path)
.components()
.next();
if let Some(path) = path {
match path {
Component::CurDir
| Component::Normal(_) => {
let path = profile.join(project.path);
write(
&path,
&file,
&state.io_semaphore,
)
.await?;
}
_ => {}
};
}
Ok(())
}
},
)
.await?;
let extract_overrides = |overrides: String| async {
let reader = Cursor::new(&file);
let mut overrides_zip =
ZipFileReader::new(reader).await.map_err(|_| {
crate::Error::from(crate::ErrorKind::InputError(
"Failed extract overrides Zip".to_string(),
))
})?;
let profile = profile.clone();
async move {
for index in 0..overrides_zip.file().entries().len() {
let file = overrides_zip
.file()
.entries()
.get(index)
.unwrap()
.entry()
.clone();
let file_path = PathBuf::from(file.filename());
if file.filename().starts_with(&overrides)
&& !file.filename().ends_with('/')
{
// Reads the file into the 'content' variable
let mut content = Vec::new();
let mut reader =
overrides_zip.entry(index).await?;
reader
.read_to_end_checked(&mut content, &file)
.await?;
let mut new_path = PathBuf::new();
let components = file_path.components().skip(1);
for component in components {
new_path.push(component);
}
if new_path.file_name().is_some() {
write(
&profile.join(new_path),
&content,
&state.io_semaphore,
)
.await?;
}
}
}
Ok::<(), crate::Error>(())
}
.await
};
emit_loading(&loading_bar, 0.0, Some("Extracting overrides"))
.await?;
extract_overrides("overrides".to_string()).await?;
extract_overrides("client_overrides".to_string()).await?;
emit_loading(
&loading_bar,
29.9,
Some("Done extacting overrides"),
)
.await?;
if let Some(profile) =
crate::api::profile::get(&profile).await?
{
tokio::try_join!(
super::profile::sync(&profile.path),
crate::launcher::install_minecraft(
&profile,
Some(loading_bar)
),
)?;
}
Ok::<PathBuf, crate::Error>(profile)
}
.await;
Ok::<PathBuf, crate::Error>(profile)
}
.await;
match result {
Ok(profile) => Ok(profile),
Err(err) => {
let _ = crate::api::profile::remove(&profile_raw).await;
match result {
Ok(profile) => Ok(profile),
Err(err) => {
let _ = crate::api::profile::remove(&profile_raw).await;
Err(err)
Err(err)
}
}
} else {
Err(crate::Error::from(crate::ErrorKind::InputError(
"No pack manifest found in mrpack".to_string(),
)))
}
} else {
Err(crate::Error::from(crate::ErrorKind::InputError(
"No pack manifest found in mrpack".to_string(),
)))
}
})
.await
}

View File

@@ -139,41 +139,44 @@ pub async fn install(path: &Path) -> crate::Result<()> {
pub async fn update_all(profile_path: &Path) -> crate::Result<()> {
let state = State::get().await?;
let mut profiles = state.profiles.write().await;
Box::pin(async move {
let mut profiles = state.profiles.write().await;
if let Some(profile) = profiles.0.get_mut(profile_path) {
let loading_bar = init_loading(
LoadingBarType::ProfileUpdate {
profile_uuid: profile.uuid,
profile_name: profile.metadata.name.clone(),
},
100.0,
"Updating profile",
)
.await?;
if let Some(profile) = profiles.0.get_mut(profile_path) {
let loading_bar = init_loading(
LoadingBarType::ProfileUpdate {
profile_uuid: profile.uuid,
profile_name: profile.metadata.name.clone(),
},
100.0,
"Updating profile",
)
.await?;
use futures::StreamExt;
loading_try_for_each_concurrent(
futures::stream::iter(profile.projects.keys())
.map(Ok::<&PathBuf, crate::Error>),
None,
Some(&loading_bar),
100.0,
profile.projects.keys().len(),
None,
|project| update_project(profile_path, project, Some(true)),
)
.await?;
use futures::StreamExt;
loading_try_for_each_concurrent(
futures::stream::iter(profile.projects.keys())
.map(Ok::<&PathBuf, crate::Error>),
None,
Some(&loading_bar),
100.0,
profile.projects.keys().len(),
None,
|project| update_project(profile_path, project, Some(true)),
)
.await?;
profile.sync().await?;
profile.sync().await?;
Ok(())
} else {
Err(crate::ErrorKind::UnmanagedProfileError(
profile_path.display().to_string(),
)
.as_error())
}
Ok(())
} else {
Err(crate::ErrorKind::UnmanagedProfileError(
profile_path.display().to_string(),
)
.as_error())
}
})
.await
}
pub async fn update_project(
@@ -372,146 +375,148 @@ pub async fn run_credentials(
path: &Path,
credentials: &auth::Credentials,
) -> crate::Result<Arc<RwLock<MinecraftChild>>> {
let state = State::get().await?;
let settings = state.settings.read().await;
let metadata = state.metadata.read().await;
let profile = get(path).await?.ok_or_else(|| {
crate::ErrorKind::OtherError(format!(
"Tried to run a nonexistent or unloaded profile at path {}!",
path.display()
))
})?;
let version = metadata
.minecraft
.versions
.iter()
.find(|it| it.id == profile.metadata.game_version)
.ok_or_else(|| {
crate::ErrorKind::LauncherError(format!(
"Invalid or unknown Minecraft version: {}",
profile.metadata.game_version
Box::pin(async move {
let state = State::get().await?;
let settings = state.settings.read().await;
let metadata = state.metadata.read().await;
let profile = get(path).await?.ok_or_else(|| {
crate::ErrorKind::OtherError(format!(
"Tried to run a nonexistent or unloaded profile at path {}!",
path.display()
))
})?;
let version_info = download::download_version_info(
&state,
version,
profile.metadata.loader_version.as_ref(),
None,
None,
)
.await?;
let pre_launch_hooks =
&profile.hooks.as_ref().unwrap_or(&settings.hooks).pre_launch;
if let Some(hook) = pre_launch_hooks {
// TODO: hook parameters
let mut cmd = hook.split(' ');
if let Some(command) = cmd.next() {
let result = Command::new(command)
.args(&cmd.collect::<Vec<&str>>())
.current_dir(path)
.spawn()?
.wait()
.await?;
if !result.success() {
return Err(crate::ErrorKind::LauncherError(format!(
"Non-zero exit code for pre-launch hook: {}",
result.code().unwrap_or(-1)
let version = metadata
.minecraft
.versions
.iter()
.find(|it| it.id == profile.metadata.game_version)
.ok_or_else(|| {
crate::ErrorKind::LauncherError(format!(
"Invalid or unknown Minecraft version: {}",
profile.metadata.game_version
))
.as_error());
})?;
let version_info = download::download_version_info(
&state,
version,
profile.metadata.loader_version.as_ref(),
None,
None,
)
.await?;
let pre_launch_hooks =
&profile.hooks.as_ref().unwrap_or(&settings.hooks).pre_launch;
if let Some(hook) = pre_launch_hooks {
// TODO: hook parameters
let mut cmd = hook.split(' ');
if let Some(command) = cmd.next() {
let result = Command::new(command)
.args(&cmd.collect::<Vec<&str>>())
.current_dir(path)
.spawn()?
.wait()
.await?;
if !result.success() {
return Err(crate::ErrorKind::LauncherError(format!(
"Non-zero exit code for pre-launch hook: {}",
result.code().unwrap_or(-1)
))
.as_error());
}
}
}
}
let java_version = match profile.java {
// Load profile-specific Java implementation choice
// (This defaults to Daedalus-decided key on init, but can be changed by the user)
Some(JavaSettings {
jre_key: Some(ref jre_key),
..
}) => settings.java_globals.get(jre_key),
// Fall back to Daedalus-decided key if no profile-specific key is set
_ => {
match version_info
.java_version
.as_ref()
.map(|it| it.major_version)
.unwrap_or(0)
{
0..=16 => settings
.java_globals
.get(&crate::jre::JAVA_8_KEY.to_string()),
17 => settings
.java_globals
.get(&crate::jre::JAVA_17_KEY.to_string()),
_ => settings
.java_globals
.get(&crate::jre::JAVA_18PLUS_KEY.to_string()),
let java_version = match profile.java {
// Load profile-specific Java implementation choice
// (This defaults to Daedalus-decided key on init, but can be changed by the user)
Some(JavaSettings {
jre_key: Some(ref jre_key),
..
}) => settings.java_globals.get(jre_key),
// Fall back to Daedalus-decided key if no profile-specific key is set
_ => {
match version_info
.java_version
.as_ref()
.map(|it| it.major_version)
.unwrap_or(0)
{
0..=16 => settings
.java_globals
.get(&crate::jre::JAVA_8_KEY.to_string()),
17 => settings
.java_globals
.get(&crate::jre::JAVA_17_KEY.to_string()),
_ => settings
.java_globals
.get(&crate::jre::JAVA_18PLUS_KEY.to_string()),
}
}
};
let java_version = java_version.as_ref().ok_or_else(|| {
crate::ErrorKind::LauncherError(format!(
"No Java stored for version {}",
version_info.java_version.map_or(8, |it| it.major_version),
))
})?;
// Get the path to the Java executable from the chosen Java implementation key
let java_install: &Path = &PathBuf::from(&java_version.path);
if !java_install.exists() {
return Err(crate::ErrorKind::LauncherError(format!(
"Could not find Java install: {}",
java_install.display()
))
.as_error());
}
};
let java_version = java_version.as_ref().ok_or_else(|| {
crate::ErrorKind::LauncherError(format!(
"No Java stored for version {}",
version_info.java_version.map_or(8, |it| it.major_version),
))
})?;
let java_args = profile
.java
.as_ref()
.and_then(|it| it.extra_arguments.as_ref())
.unwrap_or(&settings.custom_java_args);
// Get the path to the Java executable from the chosen Java implementation key
let java_install: &Path = &PathBuf::from(&java_version.path);
if !java_install.exists() {
return Err(crate::ErrorKind::LauncherError(format!(
"Could not find Java install: {}",
java_install.display()
))
.as_error());
}
let java_args = profile
.java
.as_ref()
.and_then(|it| it.extra_arguments.as_ref())
.unwrap_or(&settings.custom_java_args);
let wrapper = profile
.hooks
.as_ref()
.map_or(&settings.hooks.wrapper, |it| &it.wrapper);
let wrapper = profile
.hooks
.as_ref()
.map_or(&settings.hooks.wrapper, |it| &it.wrapper);
let memory = profile.memory.unwrap_or(settings.memory);
let resolution = profile.resolution.unwrap_or(settings.game_resolution);
let memory = profile.memory.unwrap_or(settings.memory);
let resolution = profile.resolution.unwrap_or(settings.game_resolution);
let env_args = &settings.custom_env_args;
let env_args = &settings.custom_env_args;
// Post post exit hooks
let post_exit_hook =
&profile.hooks.as_ref().unwrap_or(&settings.hooks).post_exit;
// Post post exit hooks
let post_exit_hook =
&profile.hooks.as_ref().unwrap_or(&settings.hooks).post_exit;
let post_exit_hook = if let Some(hook) = post_exit_hook {
let mut cmd = hook.split(' ');
if let Some(command) = cmd.next() {
let mut command = Command::new(command);
command.args(&cmd.collect::<Vec<&str>>()).current_dir(path);
Some(command)
let post_exit_hook = if let Some(hook) = post_exit_hook {
let mut cmd = hook.split(' ');
if let Some(command) = cmd.next() {
let mut command = Command::new(command);
command.args(&cmd.collect::<Vec<&str>>()).current_dir(path);
Some(command)
} else {
None
}
} else {
None
}
} else {
None
};
};
let mc_process = crate::launcher::launch_minecraft(
java_install,
java_args,
env_args,
wrapper,
&memory,
&resolution,
credentials,
post_exit_hook,
&profile,
)
.await?;
Ok(mc_process)
let mc_process = crate::launcher::launch_minecraft(
java_install,
java_args,
env_args,
wrapper,
&memory,
&resolution,
credentials,
post_exit_hook,
&profile,
)
.await?;
Ok(mc_process)
})
.await
}

View File

@@ -51,153 +51,156 @@ pub async fn profile_create(
) -> crate::Result<PathBuf> {
trace!("Creating new profile. {}", name);
let state = State::get().await?;
let metadata = state.metadata.read().await;
Box::pin(async move {
let metadata = state.metadata.read().await;
let uuid = Uuid::new_v4();
let path = state.directories.profiles_dir().join(uuid.to_string());
if path.exists() {
if !path.is_dir() {
return Err(ProfileCreationError::NotFolder.into());
}
if path.join("profile.json").exists() {
return Err(ProfileCreationError::ProfileExistsError(
path.join("profile.json"),
)
.into());
}
if ReadDirStream::new(fs::read_dir(&path).await?)
.next()
.await
.is_some()
{
return Err(ProfileCreationError::NotEmptyFolder.into());
}
} else {
fs::create_dir_all(&path).await?;
}
info!(
"Creating profile at path {}",
&canonicalize(&path)?.display()
);
let loader = modloader;
let loader = if loader != ModLoader::Vanilla {
let version = loader_version.unwrap_or_else(|| "latest".to_string());
let filter = |it: &LoaderVersion| match version.as_str() {
"latest" => true,
"stable" => it.stable,
id => it.id == *id || format!("{}-{}", game_version, id) == it.id,
};
let loader_data = match loader {
ModLoader::Forge => &metadata.forge,
ModLoader::Fabric => &metadata.fabric,
_ => {
return Err(ProfileCreationError::NoManifest(
loader.to_string(),
)
.into())
let uuid = Uuid::new_v4();
let path = state.directories.profiles_dir().join(uuid.to_string());
if path.exists() {
if !path.is_dir() {
return Err(ProfileCreationError::NotFolder.into());
}
if path.join("profile.json").exists() {
return Err(ProfileCreationError::ProfileExistsError(
path.join("profile.json"),
)
.into());
}
if ReadDirStream::new(fs::read_dir(&path).await?)
.next()
.await
.is_some()
{
return Err(ProfileCreationError::NotEmptyFolder.into());
}
} else {
fs::create_dir_all(&path).await?;
}
info!(
"Creating profile at path {}",
&canonicalize(&path)?.display()
);
let loader = modloader;
let loader = if loader != ModLoader::Vanilla {
let version = loader_version.unwrap_or_else(|| "latest".to_string());
let filter = |it: &LoaderVersion| match version.as_str() {
"latest" => true,
"stable" => it.stable,
id => it.id == *id || format!("{}-{}", game_version, id) == it.id,
};
let loader_data = match loader {
ModLoader::Forge => &metadata.forge,
ModLoader::Fabric => &metadata.fabric,
_ => {
return Err(ProfileCreationError::NoManifest(
loader.to_string(),
)
.into())
}
};
let loaders = &loader_data
.game_versions
.iter()
.find(|it| {
it.id.replace(
daedalus::modded::DUMMY_REPLACE_STRING,
&game_version,
) == game_version
})
.ok_or_else(|| {
ProfileCreationError::ModloaderUnsupported(
loader.to_string(),
game_version.clone(),
)
})?
.loaders;
let loader_version = loaders
.iter()
.cloned()
.find(filter)
.or(
// If stable was searched for but not found, return latest by default
if version == "stable" {
loaders.iter().next().cloned()
} else {
None
},
)
.ok_or_else(|| {
ProfileCreationError::InvalidVersionModloader(
version,
loader.to_string(),
)
})?;
Some((loader_version, loader))
} else {
None
};
let loaders = &loader_data
.game_versions
.iter()
.find(|it| {
it.id.replace(
daedalus::modded::DUMMY_REPLACE_STRING,
&game_version,
) == game_version
})
.ok_or_else(|| {
ProfileCreationError::ModloaderUnsupported(
loader.to_string(),
game_version.clone(),
// Fully canonicalize now that its created for storing purposes
let path = canonicalize(&path)?;
let mut profile =
Profile::new(uuid, name, game_version, path.clone()).await?;
if let Some(ref icon) = icon {
let bytes = tokio::fs::read(icon).await?;
profile
.set_icon(
&state.directories.caches_dir(),
&state.io_semaphore,
bytes::Bytes::from(bytes),
&icon.to_string_lossy(),
)
})?
.loaders;
let loader_version = loaders
.iter()
.cloned()
.find(filter)
.or(
// If stable was searched for but not found, return latest by default
if version == "stable" {
loaders.iter().next().cloned()
} else {
None
},
)
.ok_or_else(|| {
ProfileCreationError::InvalidVersionModloader(
version,
loader.to_string(),
)
})?;
Some((loader_version, loader))
} else {
None
};
// Fully canonicalize now that its created for storing purposes
let path = canonicalize(&path)?;
let mut profile =
Profile::new(uuid, name, game_version, path.clone()).await?;
if let Some(ref icon) = icon {
let bytes = tokio::fs::read(icon).await?;
profile
.set_icon(
&state.directories.caches_dir(),
&state.io_semaphore,
bytes::Bytes::from(bytes),
&icon.to_string_lossy(),
)
.await?;
}
if let Some((loader_version, loader)) = loader {
profile.metadata.loader = loader;
profile.metadata.loader_version = Some(loader_version);
}
profile.metadata.linked_data = linked_data;
// Attempts to find optimal JRE for the profile from the JavaGlobals
// Finds optimal key, and see if key has been set in JavaGlobals
let settings = state.settings.read().await;
let optimal_version_key = jre::get_optimal_jre_key(&profile).await?;
if settings.java_globals.get(&optimal_version_key).is_some() {
profile.java = Some(JavaSettings {
jre_key: Some(optimal_version_key),
extra_arguments: None,
});
} else {
emit_warning(&format!("Could not detect optimal JRE: {optimal_version_key}, falling back to system default.")).await?;
}
emit_profile(
uuid,
path.clone(),
&profile.metadata.name,
ProfilePayloadType::Created,
)
.await?;
{
let mut profiles = state.profiles.write().await;
profiles.insert(profile.clone()).await?;
}
if !skip_install_profile.unwrap_or(false) {
crate::launcher::install_minecraft(&profile, None).await?;
}
State::sync().await?;
Ok(path)
.await?;
}
if let Some((loader_version, loader)) = loader {
profile.metadata.loader = loader;
profile.metadata.loader_version = Some(loader_version);
}
profile.metadata.linked_data = linked_data;
// Attempts to find optimal JRE for the profile from the JavaGlobals
// Finds optimal key, and see if key has been set in JavaGlobals
let settings = state.settings.read().await;
let optimal_version_key = jre::get_optimal_jre_key(&profile).await?;
if settings.java_globals.get(&optimal_version_key).is_some() {
profile.java = Some(JavaSettings {
jre_key: Some(optimal_version_key),
extra_arguments: None,
});
} else {
emit_warning(&format!("Could not detect optimal JRE: {optimal_version_key}, falling back to system default.")).await?;
}
emit_profile(
uuid,
path.clone(),
&profile.metadata.name,
ProfilePayloadType::Created,
)
.await?;
{
let mut profiles = state.profiles.write().await;
profiles.insert(profile.clone()).await?;
}
if !skip_install_profile.unwrap_or(false) {
crate::launcher::install_minecraft(&profile, None).await?;
}
State::sync().await?;
Ok(path)
}).await
}
#[derive(thiserror::Error, Debug)]