OneLauncher-Cracked/packages/oneclient_core/src/migration/mod.rs
2026-08-08 11:33:24 +07:00

142 lines
4 KiB
Rust

pub mod oneclient_v1;
pub mod vanilla;
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use crate::LauncherResult;
use oneclient_common::domain::GameLoader;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum MigrationSource {
OneClientV1,
Vanilla,
}
impl MigrationSource {
pub const ALL: &'static [MigrationSource] =
&[MigrationSource::OneClientV1, MigrationSource::Vanilla];
pub fn id(self) -> &'static str {
match self {
MigrationSource::OneClientV1 => "oneclient_v1",
MigrationSource::Vanilla => "vanilla",
}
}
pub fn display_name(self) -> &'static str {
match self {
MigrationSource::OneClientV1 => "OneClient",
MigrationSource::Vanilla => "Minecraft",
}
}
pub fn from_id(id: &str) -> Option<MigrationSource> {
Self::ALL.iter().copied().find(|s| s.id() == id)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SourceInstance {
/// Source-local identifier (v1 the cluster row id)
/// Purely informational
pub instance_id: i64,
pub folder_name: String,
pub mc_version: String,
/// Set for "migrated" clusters (e.g. 26.1 fabric -> 26.1.2 fabric)
pub target_mc_version: Option<String>,
pub mc_loader: GameLoader,
/// Empty when the source has no category concept
pub categories: Vec<String>,
pub has_game_dir: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct MigrationDetection {
pub source: MigrationSource,
pub root: PathBuf,
pub instances: Vec<SourceInstance>,
}
impl SourceInstance {
#[must_use]
pub fn import_version(&self) -> &str {
self.target_mc_version.as_deref().unwrap_or(&self.mc_version)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ImportTarget {
/// Copy into the shared `<launcher_dir>/.minecraft`
Shared,
/// Copy into the new cluster's own folder and mark it dedicated so
/// `Cluster::game_dir()` resolves there instead of the shared dir
Dedicated { new_cluster_id: i64 },
}
#[tracing::instrument]
pub async fn detect() -> LauncherResult<Option<MigrationDetection>> {
for source in MigrationSource::ALL.iter().copied() {
let detection = match source {
MigrationSource::OneClientV1 => oneclient_v1::detect().await?,
MigrationSource::Vanilla => vanilla::detect().await?,
};
if let Some(detection) = detection
&& !detection.instances.is_empty()
{
return Ok(Some(detection));
}
}
Ok(None)
}
#[tracing::instrument(skip(state))]
pub async fn import_game_dir(
state: &std::sync::Arc<crate::LauncherState>,
source: MigrationSource,
folder_name: &str,
target: ImportTarget,
) -> LauncherResult<()> {
match source {
MigrationSource::OneClientV1 => {
oneclient_v1::import_game_dir(state, folder_name, target).await
}
MigrationSource::Vanilla => vanilla::import_game_dir(state, target).await,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn vanilla_is_the_last_resort() {
assert_eq!(MigrationSource::ALL.last(), Some(&MigrationSource::Vanilla));
}
#[test]
fn import_version_prefers_resolved_target() {
let mut instance = SourceInstance {
instance_id: 1,
folder_name: "26.1 fabric".to_string(),
mc_version: "26.1".to_string(),
target_mc_version: None,
mc_loader: GameLoader::Fabric,
categories: Vec::new(),
has_game_dir: true,
};
assert_eq!(instance.import_version(), "26.1");
instance.target_mc_version = Some("26.1.2".to_string());
assert_eq!(instance.import_version(), "26.1.2");
}
#[test]
fn ids_round_trip() {
for source in MigrationSource::ALL.iter().copied() {
assert_eq!(MigrationSource::from_id(source.id()), Some(source));
}
}
}