Axolotl desktop CI / guardrails (push) Has been cancelled
Axolotl desktop CI / desktop (macos-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (ubuntu-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (windows-latest) (push) Has been cancelled
Axolotl desktop CI / website (push) Has been cancelled
Repository checks / typos (push) Has been cancelled
Repository checks / tombi (push) Has been cancelled
Rust checks / shear (push) Has been cancelled
Sync source to CNB / Sync Git ref (push) Has been cancelled
189 lines
5.3 KiB
Rust
189 lines
5.3 KiB
Rust
use crate::event::InstancePayloadType;
|
|
use crate::event::emit::emit_instance;
|
|
use crate::state::instances::adapters::sqlite::instance_rows;
|
|
use crate::state::{
|
|
CreateDirectLinkInstance, CreateInstance, EditInstance, InstanceLink,
|
|
InstanceMetadata, ModLoader, State,
|
|
};
|
|
use crate::util::{fetch::write_cached_icon, io};
|
|
use std::path::Path;
|
|
|
|
#[tracing::instrument]
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub(crate) async fn create(
|
|
name: String,
|
|
game_version: String,
|
|
modloader: ModLoader,
|
|
loader_version: Option<String>,
|
|
icon_path: Option<String>,
|
|
link: InstanceLink,
|
|
symlink_target: Option<String>,
|
|
game_dir_override: Option<String>,
|
|
) -> crate::Result<InstanceMetadata> {
|
|
let state = State::get().await?;
|
|
let instance = crate::state::create_instance(
|
|
CreateInstance {
|
|
name,
|
|
path: None,
|
|
game_version,
|
|
loader: modloader,
|
|
loader_version,
|
|
icon_path,
|
|
link,
|
|
symlink_target,
|
|
game_dir_override,
|
|
},
|
|
&state,
|
|
)
|
|
.await?;
|
|
|
|
let result = async {
|
|
emit_instance(&instance.id, InstancePayloadType::Created).await?;
|
|
|
|
crate::state::get_instance(&instance.id, &state.pool)
|
|
.await?
|
|
.ok_or_else(|| {
|
|
crate::ErrorKind::InputError(
|
|
"Created instance could not be loaded".to_string(),
|
|
)
|
|
.into()
|
|
})
|
|
}
|
|
.await;
|
|
|
|
if result.is_err() {
|
|
let _ = crate::state::remove_instance(&instance.id, &state).await;
|
|
}
|
|
|
|
result
|
|
}
|
|
|
|
/// Creates a "directly associated" instance that launches an externally
|
|
/// managed (HMCL/PCL) local version in place: nothing is copied, symlinked,
|
|
/// or reinstalled, and the game directory points at the linked `.minecraft`.
|
|
#[tracing::instrument]
|
|
pub async fn create_with_direct_link(
|
|
input: CreateDirectLinkInstance,
|
|
) -> crate::Result<InstanceMetadata> {
|
|
let state = State::get().await?;
|
|
let instance =
|
|
crate::state::create_direct_link_instance(input, &state).await?;
|
|
|
|
let result = async {
|
|
emit_instance(&instance.id, InstancePayloadType::Created).await?;
|
|
|
|
crate::state::get_instance(&instance.id, &state.pool)
|
|
.await?
|
|
.ok_or_else(|| {
|
|
crate::ErrorKind::InputError(
|
|
"Created instance could not be loaded".to_string(),
|
|
)
|
|
.into()
|
|
})
|
|
}
|
|
.await;
|
|
|
|
if result.is_err() {
|
|
let _ = crate::state::remove_instance(&instance.id, &state).await;
|
|
}
|
|
|
|
result
|
|
}
|
|
|
|
/// Reconcile configured external Minecraft roots with direct-link records.
|
|
pub async fn sync_direct_links(
|
|
roots: Vec<crate::state::ExternalMinecraftRoot>,
|
|
) -> crate::Result<crate::state::DirectLinkSyncReport> {
|
|
let state = State::get().await?;
|
|
crate::state::sync_direct_link_instances(roots, &state).await
|
|
}
|
|
|
|
pub async fn edit(
|
|
instance_id: &str,
|
|
patch: EditInstance,
|
|
) -> crate::Result<InstanceMetadata> {
|
|
let state = State::get().await?;
|
|
crate::state::edit_instance(instance_id, patch, &state.pool).await?;
|
|
|
|
let instance = crate::state::get_instance(instance_id, &state.pool)
|
|
.await?
|
|
.ok_or_else(|| {
|
|
crate::ErrorKind::InputError("Unknown instance".to_string())
|
|
.as_error()
|
|
})?;
|
|
|
|
emit_instance(&instance.instance.id, InstancePayloadType::Edited).await?;
|
|
|
|
Ok(instance)
|
|
}
|
|
|
|
pub async fn cache_icon(
|
|
icon_name: &str,
|
|
bytes: Vec<u8>,
|
|
) -> crate::Result<String> {
|
|
let state = State::get().await?;
|
|
let path = write_cached_icon(
|
|
icon_name,
|
|
&state.directories.caches_dir(),
|
|
bytes::Bytes::from(bytes),
|
|
&state.io_semaphore,
|
|
)
|
|
.await?;
|
|
|
|
Ok(path.to_string_lossy().to_string())
|
|
}
|
|
|
|
pub async fn edit_icon(
|
|
instance_id: &str,
|
|
icon_path: Option<&Path>,
|
|
) -> crate::Result<()> {
|
|
let state = State::get().await?;
|
|
let instance =
|
|
instance_rows::get_instance_display_info(instance_id, &state.pool)
|
|
.await?
|
|
.ok_or_else(|| {
|
|
crate::ErrorKind::InputError("Unknown instance".to_string())
|
|
})?;
|
|
let icon_path = if let Some(icon) = icon_path {
|
|
let bytes = io::read(icon).await?;
|
|
let file = crate::util::fetch::write_cached_icon(
|
|
&icon.to_string_lossy(),
|
|
&state.directories.caches_dir(),
|
|
bytes::Bytes::from(bytes),
|
|
&state.io_semaphore,
|
|
)
|
|
.await?;
|
|
Some(file.to_string_lossy().to_string())
|
|
} else {
|
|
None
|
|
};
|
|
|
|
crate::state::edit_instance(
|
|
instance_id,
|
|
EditInstance {
|
|
icon_path: Some(icon_path),
|
|
..EditInstance::default()
|
|
},
|
|
&state.pool,
|
|
)
|
|
.await?;
|
|
emit_instance(&instance.id, InstancePayloadType::Edited).await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[tracing::instrument]
|
|
pub async fn remove(instance_id: &str) -> crate::Result<()> {
|
|
let state = State::get().await?;
|
|
let instance =
|
|
instance_rows::get_instance_display_info(instance_id, &state.pool)
|
|
.await?;
|
|
crate::state::remove_instance(instance_id, &state).await?;
|
|
|
|
if let Some(instance) = instance {
|
|
emit_instance(&instance.id, InstancePayloadType::Removed).await?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|