Files
Starlight_Lancher/packages/app-lib/src/api/instance/lifecycle.rs
T
Disy d2a319a4d5
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
fix: 启用启动器更新并修复初始化竞态
2026-09-26 13:06:51 +08:00

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(())
}