Mod Management API (#81)

* Profile mod management

* remove print statement
This commit is contained in:
Geometrically
2023-04-13 12:03:15 -07:00
committed by GitHub
parent bb126c0545
commit f8173d3b78
22 changed files with 616 additions and 252 deletions

View File

@@ -46,7 +46,7 @@ pub async fn authenticate(
))
})?;
let credentials = flow.extract_credentials().await?;
let credentials = flow.extract_credentials(&state.io_semaphore).await?;
users.insert(&credentials)?;
if state.settings.read().await.default_user.is_none() {
@@ -60,13 +60,11 @@ pub async fn authenticate(
/// Refresh some credentials using Hydra, if needed
/// This is the primary desired way to get credentials, as it will also refresh them.
#[tracing::instrument]
pub async fn refresh(
user: uuid::Uuid,
update_name: bool,
) -> crate::Result<Credentials> {
pub async fn refresh(user: uuid::Uuid) -> crate::Result<Credentials> {
let state = State::get().await?;
let mut users = state.users.write().await;
let io_sempahore = &state.io_semaphore;
futures::future::ready(users.get(user)?.ok_or_else(|| {
crate::ErrorKind::OtherError(format!(
"Tried to refresh nonexistent user with ID {user}"
@@ -75,10 +73,7 @@ pub async fn refresh(
}))
.and_then(|mut credentials| async move {
if chrono::offset::Utc::now() > credentials.expires {
inner::refresh_credentials(&mut credentials).await?;
if update_name {
inner::refresh_username(&mut credentials).await?;
}
inner::refresh_credentials(&mut credentials, io_sempahore).await?;
}
users.insert(&credentials)?;
Ok(credentials)

View File

@@ -79,6 +79,7 @@ pub async fn install_pack_from_version_id(
Method::GET,
&format!("{}version/{}", MODRINTH_API_URL, version_id),
None,
None,
&state.io_semaphore,
)
.await?;
@@ -104,6 +105,7 @@ pub async fn install_pack_from_version_id(
Method::GET,
&format!("{}project/{}", MODRINTH_API_URL, version.project_id),
None,
None,
&state.io_semaphore,
)
.await?;
@@ -230,7 +232,7 @@ async fn install_pack(
let profile = profile.clone();
async move {
// TODO: Future update: prompt user for optional files in a modpack
//TODO: Future update: prompt user for optional files in a modpack
if let Some(env) = project.env {
if env
.get(&EnvType::Client)

View File

@@ -13,7 +13,7 @@ use std::{
path::{Path, PathBuf},
sync::Arc,
};
use tokio::{process::Command, sync::RwLock};
use tokio::{fs, process::Command, sync::RwLock};
/// Remove a profile
#[tracing::instrument]
@@ -67,23 +67,10 @@ pub async fn list() -> crate::Result<std::collections::HashMap<PathBuf, Profile>
#[tracing::instrument]
pub async fn sync(path: &Path) -> crate::Result<()> {
let state = State::get().await?;
let mut profiles = state.profiles.write().await;
if let Some(profile) = get(path).await? {
let paths = profile.get_profile_project_paths()?;
let projects = crate::state::infer_data_from_files(
paths,
state.directories.caches_dir(),
&state.io_semaphore,
)
.await?;
{
let mut profiles = state.profiles.write().await;
if let Some(profile) = profiles.0.get_mut(path) {
profile.projects = projects;
}
}
if let Some(profile) = profiles.0.get_mut(path) {
profile.sync().await?;
State::sync().await?;
Ok(())
@@ -95,6 +82,99 @@ pub async fn sync(path: &Path) -> crate::Result<()> {
}
}
/// Add a project from a version
#[tracing::instrument]
pub async fn add_project_from_version(
profile: &Path,
version_id: String,
) -> crate::Result<PathBuf> {
let state = State::get().await?;
let mut profiles = state.profiles.write().await;
if let Some(profile) = profiles.0.get_mut(profile) {
profile.add_project_version(version_id).await
} else {
Err(crate::ErrorKind::UnmanagedProfileError(
profile.display().to_string(),
)
.as_error())
}
}
/// Add a project from an FS path
#[tracing::instrument]
pub async fn add_project_from_path(
profile: &Path,
path: &Path,
project_type: Option<String>,
) -> crate::Result<PathBuf> {
let state = State::get().await?;
let mut profiles = state.profiles.write().await;
if let Some(profile) = profiles.0.get_mut(profile) {
let file = fs::read(path).await?;
let file_name = path
.file_name()
.unwrap_or_default()
.to_string_lossy()
.to_string();
profile
.add_project_bytes(
&file_name,
bytes::Bytes::from(file),
project_type.and_then(|x| serde_json::from_str(&x).ok()),
)
.await
} else {
Err(crate::ErrorKind::UnmanagedProfileError(
profile.display().to_string(),
)
.as_error())
}
}
/// Toggle whether a project is disabled or not
#[tracing::instrument]
pub async fn toggle_disable_project(
profile: &Path,
project: &Path,
) -> crate::Result<()> {
let state = State::get().await?;
let mut profiles = state.profiles.write().await;
if let Some(profile) = profiles.0.get_mut(profile) {
profile.toggle_disable_project(project).await?;
Ok(())
} else {
Err(crate::ErrorKind::UnmanagedProfileError(
profile.display().to_string(),
)
.as_error())
}
}
/// Remove a project from a profile
#[tracing::instrument]
pub async fn remove_project(
profile: &Path,
project: &Path,
) -> crate::Result<()> {
let state = State::get().await?;
let mut profiles = state.profiles.write().await;
if let Some(profile) = profiles.0.get_mut(profile) {
profile.remove_project(project).await?;
Ok(())
} else {
Err(crate::ErrorKind::UnmanagedProfileError(
profile.display().to_string(),
)
.as_error())
}
}
/// Run Minecraft using a profile and the default credentials, logged in credentials,
/// failing with an error if no credentials are available
#[tracing::instrument(skip_all)]
@@ -104,13 +184,13 @@ pub async fn run(path: &Path) -> crate::Result<Arc<RwLock<MinecraftChild>>> {
// Get default account and refresh credentials (preferred way to log in)
let default_account = state.settings.read().await.default_user;
let credentials = if let Some(default_account) = default_account {
refresh(default_account, false).await?
refresh(default_account).await?
} else {
// If no default account, try to use a logged in account
let users = auth::users().await?;
let last_account = users.iter().next();
if let Some(last_account) = last_account {
refresh(last_account.id, false).await?
refresh(last_account.id).await?
} else {
return Err(crate::ErrorKind::NoCredentialsError.as_error());
}
@@ -123,7 +203,7 @@ pub async fn run(path: &Path) -> crate::Result<Arc<RwLock<MinecraftChild>>> {
#[tracing::instrument(skip_all)]
pub async fn run_credentials(
path: &Path,
credentials: &crate::auth::Credentials,
credentials: &auth::Credentials,
) -> crate::Result<Arc<RwLock<MinecraftChild>>> {
let state = State::get().await?;
let settings = state.settings.read().await;

View File

@@ -159,10 +159,10 @@ pub async fn profile_create(
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.set_java_settings(Some(JavaSettings {
profile.java = Some(JavaSettings {
jre_key: Some(optimal_version_key),
extra_arguments: None,
}))?;
});
} else {
println!("Could not detect optimal JRE: {optimal_version_key}, falling back to system default.");
}