813 lines
27 KiB
Rust
813 lines
27 KiB
Rust
use crate::data::{Dependency, ProjectType, User, Version};
|
|
use crate::jre::check_jre;
|
|
use crate::prelude::ModLoader;
|
|
use crate::state;
|
|
use crate::state::{
|
|
CacheValue, CachedEntry, CachedFile, CachedFileHash, CachedFileUpdate,
|
|
Credentials, DefaultPage, DependencyType, DeviceToken, DeviceTokenKey,
|
|
DeviceTokenPair, FileType, Hooks, LauncherFeatureVersion, LinkedData,
|
|
MemorySettings, ModrinthCredentials, Profile, ProfileInstallStage,
|
|
TeamMember, Theme, VersionFile, WindowSize,
|
|
};
|
|
use crate::util::fetch::{IoSemaphore, read_json};
|
|
use chrono::{DateTime, Utc};
|
|
use p256::ecdsa::SigningKey;
|
|
use p256::pkcs8::DecodePrivateKey;
|
|
use serde::Deserialize;
|
|
use std::collections::HashMap;
|
|
use std::path::PathBuf;
|
|
use tokio::sync::Semaphore;
|
|
use uuid::Uuid;
|
|
|
|
use super::MinecraftProfile;
|
|
|
|
pub async fn migrate_legacy_data<'a, E>(exec: E) -> crate::Result<()>
|
|
where
|
|
E: sqlx::Executor<'a, Database = sqlx::Sqlite> + Copy,
|
|
{
|
|
let mut settings = state::Settings::get(exec).await?;
|
|
|
|
if settings.migrated {
|
|
return Ok(());
|
|
};
|
|
|
|
let Some(old_launcher_root) = default_settings_dir() else {
|
|
return Ok(());
|
|
};
|
|
let old_launcher_root_str = old_launcher_root.to_string_lossy().to_string();
|
|
|
|
let io_semaphore = IoSemaphore(Semaphore::new(10));
|
|
let settings_path = old_launcher_root.join("settings.json");
|
|
|
|
if let Ok(legacy_settings) =
|
|
read_json::<LegacySettings>(&settings_path, &io_semaphore).await
|
|
{
|
|
settings.max_concurrent_writes = legacy_settings.max_concurrent_writes;
|
|
settings.max_concurrent_downloads =
|
|
legacy_settings.max_concurrent_downloads;
|
|
settings.theme = match legacy_settings.theme {
|
|
LegacyTheme::Dark => Theme::Dark,
|
|
LegacyTheme::Light => Theme::Light,
|
|
LegacyTheme::Oled => Theme::Oled,
|
|
};
|
|
settings.default_page = match legacy_settings.default_page {
|
|
LegacyDefaultPage::Home => DefaultPage::Home,
|
|
LegacyDefaultPage::Library => DefaultPage::Library,
|
|
};
|
|
settings.collapsed_navigation = legacy_settings.collapsed_navigation;
|
|
settings.advanced_rendering = legacy_settings.advanced_rendering;
|
|
settings.native_decorations = legacy_settings.native_decorations;
|
|
settings.telemetry = !legacy_settings.opt_out_analytics;
|
|
settings.discord_rpc = !legacy_settings.disable_discord_rpc;
|
|
settings.developer_mode = legacy_settings.developer_mode;
|
|
settings.onboarded = legacy_settings.fully_onboarded;
|
|
settings.extra_launch_args = legacy_settings.custom_java_args;
|
|
settings.custom_env_vars = legacy_settings.custom_env_args;
|
|
settings.memory.maximum = legacy_settings.memory.maximum;
|
|
settings.force_fullscreen = legacy_settings.force_fullscreen;
|
|
settings.game_resolution.0 = legacy_settings.game_resolution.0;
|
|
settings.game_resolution.1 = legacy_settings.game_resolution.1;
|
|
settings.hide_on_process_start = legacy_settings.hide_on_process;
|
|
settings.hooks.pre_launch = legacy_settings.hooks.pre_launch;
|
|
settings.hooks.wrapper = legacy_settings.hooks.wrapper;
|
|
settings.hooks.post_exit = legacy_settings.hooks.post_exit;
|
|
|
|
if let Some(path) = legacy_settings
|
|
.loaded_config_dir
|
|
.clone()
|
|
.and_then(|x| x.to_str().map(|x| x.to_string()))
|
|
&& path != old_launcher_root_str
|
|
{
|
|
settings.custom_dir = Some(path);
|
|
}
|
|
|
|
settings.prev_custom_dir = Some(old_launcher_root_str.clone());
|
|
|
|
for (_, legacy_version) in legacy_settings.java_globals.0 {
|
|
if let Ok(java_version) =
|
|
check_jre(PathBuf::from(legacy_version.path)).await
|
|
{
|
|
java_version.upsert(exec).await?;
|
|
}
|
|
}
|
|
|
|
let modrinth_auth_path =
|
|
old_launcher_root.join("caches/metadata/auth.json");
|
|
if let Ok(creds) = read_json::<LegacyModrinthCredentials>(
|
|
&modrinth_auth_path,
|
|
&io_semaphore,
|
|
)
|
|
.await
|
|
{
|
|
ModrinthCredentials {
|
|
session: creds.session,
|
|
expires: creds.expires_at,
|
|
user_id: creds.user.id,
|
|
active: true,
|
|
}
|
|
.upsert(exec)
|
|
.await?;
|
|
}
|
|
|
|
let minecraft_auth_path =
|
|
old_launcher_root.join("caches/metadata/minecraft_auth.json");
|
|
if let Ok(minecraft_auth) = read_json::<LegacyMinecraftAuthStore>(
|
|
&minecraft_auth_path,
|
|
&io_semaphore,
|
|
)
|
|
.await
|
|
{
|
|
let minecraft_users_len = minecraft_auth.users.len();
|
|
for (uuid, legacy_credentials) in minecraft_auth.users {
|
|
Credentials {
|
|
offline_profile: MinecraftProfile {
|
|
id: legacy_credentials.id,
|
|
name: legacy_credentials.username,
|
|
..MinecraftProfile::default()
|
|
},
|
|
access_token: legacy_credentials.access_token,
|
|
refresh_token: legacy_credentials.refresh_token,
|
|
expires: legacy_credentials.expires,
|
|
active: minecraft_auth.default_user == Some(uuid)
|
|
|| minecraft_users_len == 1,
|
|
}
|
|
.upsert(exec)
|
|
.await?;
|
|
}
|
|
|
|
if let Some(device_token) = minecraft_auth.token
|
|
&& let Ok(private_key) =
|
|
SigningKey::from_pkcs8_pem(&device_token.private_key)
|
|
&& let Ok(uuid) = Uuid::parse_str(&device_token.id)
|
|
{
|
|
DeviceTokenPair {
|
|
token: DeviceToken {
|
|
issue_instant: device_token.token.issue_instant,
|
|
not_after: device_token.token.not_after,
|
|
token: device_token.token.token,
|
|
display_claims: device_token.token.display_claims,
|
|
},
|
|
key: DeviceTokenKey {
|
|
id: uuid,
|
|
key: private_key,
|
|
x: device_token.x,
|
|
y: device_token.y,
|
|
},
|
|
}
|
|
.upsert(exec)
|
|
.await?;
|
|
}
|
|
}
|
|
|
|
let mut cached_entries = vec![];
|
|
|
|
if let Ok(profiles_dir) = std::fs::read_dir(
|
|
legacy_settings
|
|
.loaded_config_dir
|
|
.clone()
|
|
.unwrap_or_else(|| old_launcher_root.clone())
|
|
.join("profiles"),
|
|
) {
|
|
for entry in profiles_dir.flatten() {
|
|
if !entry.path().is_dir() {
|
|
continue;
|
|
}
|
|
|
|
let profile_path = entry.path().join("profile.json");
|
|
|
|
let Ok(profile) =
|
|
read_json::<LegacyProfile>(&profile_path, &io_semaphore)
|
|
.await
|
|
else {
|
|
continue;
|
|
};
|
|
|
|
for (path, project) in profile.projects {
|
|
let full_path = legacy_settings
|
|
.loaded_config_dir
|
|
.clone()
|
|
.unwrap_or_else(|| old_launcher_root.clone())
|
|
.join("profiles")
|
|
.join(&profile.path)
|
|
.join(&path);
|
|
|
|
if !full_path.exists() || !full_path.is_file() {
|
|
continue;
|
|
}
|
|
let sha512 = project.sha512;
|
|
|
|
if let LegacyProjectMetadata::Modrinth {
|
|
version,
|
|
members,
|
|
update_version,
|
|
..
|
|
} = project.metadata
|
|
&& let Some(file) = version
|
|
.files
|
|
.iter()
|
|
.find(|x| x.hashes.get("sha512") == Some(&sha512))
|
|
&& let Some(sha1) = file.hashes.get("sha1")
|
|
{
|
|
if let Ok(metadata) = full_path.metadata() {
|
|
let file_name = format!(
|
|
"{}/{}",
|
|
profile.path,
|
|
path.replace('\\', "/")
|
|
.replace(".disabled", "")
|
|
);
|
|
|
|
cached_entries.push(CacheValue::FileHash(
|
|
CachedFileHash {
|
|
path: file_name,
|
|
size: metadata.len(),
|
|
hash: sha1.clone(),
|
|
project_type:
|
|
ProjectType::get_from_parent_folder(
|
|
&full_path,
|
|
),
|
|
},
|
|
));
|
|
}
|
|
|
|
cached_entries.push(CacheValue::File(CachedFile {
|
|
hash: sha1.clone(),
|
|
project_id: version.project_id.clone(),
|
|
version_id: version.id.clone(),
|
|
}));
|
|
|
|
if let Some(update_version) = update_version {
|
|
let mod_loader: ModLoader =
|
|
profile.metadata.loader.into();
|
|
cached_entries.push(CacheValue::FileUpdate(
|
|
CachedFileUpdate {
|
|
hash: sha1.clone(),
|
|
game_version: profile
|
|
.metadata
|
|
.game_version
|
|
.clone(),
|
|
loaders: vec![
|
|
mod_loader.as_str().to_string(),
|
|
],
|
|
update_version_id: update_version
|
|
.id
|
|
.clone(),
|
|
},
|
|
));
|
|
|
|
cached_entries.push(CacheValue::Version(
|
|
(*update_version).into(),
|
|
));
|
|
}
|
|
|
|
let members = members
|
|
.into_iter()
|
|
.map(|x| {
|
|
let user = User {
|
|
id: x.user.id,
|
|
username: x.user.username,
|
|
avatar_url: x.user.avatar_url,
|
|
bio: x.user.bio,
|
|
created: x.user.created,
|
|
role: x.user.role,
|
|
badges: 0,
|
|
};
|
|
|
|
cached_entries
|
|
.push(CacheValue::User(user.clone()));
|
|
|
|
TeamMember {
|
|
team_id: x.team_id,
|
|
user,
|
|
is_owner: x.role == "Owner",
|
|
role: x.role,
|
|
ordering: x.ordering,
|
|
}
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
cached_entries.push(CacheValue::Team(members));
|
|
|
|
cached_entries
|
|
.push(CacheValue::Version((*version).into()));
|
|
}
|
|
}
|
|
|
|
Profile {
|
|
path: profile.path,
|
|
install_stage: match profile.install_stage {
|
|
LegacyProfileInstallStage::Installed => {
|
|
ProfileInstallStage::Installed
|
|
}
|
|
LegacyProfileInstallStage::Installing => {
|
|
ProfileInstallStage::MinecraftInstalling
|
|
}
|
|
LegacyProfileInstallStage::PackInstalling => {
|
|
ProfileInstallStage::PackInstalling
|
|
}
|
|
LegacyProfileInstallStage::NotInstalled => {
|
|
ProfileInstallStage::NotInstalled
|
|
}
|
|
},
|
|
launcher_feature_version: LauncherFeatureVersion::None,
|
|
name: profile.metadata.name,
|
|
icon_path: profile.metadata.icon,
|
|
game_version: profile.metadata.game_version,
|
|
protocol_version: None,
|
|
loader: profile.metadata.loader.into(),
|
|
loader_version: profile
|
|
.metadata
|
|
.loader_version
|
|
.map(|x| x.id),
|
|
groups: profile.metadata.groups,
|
|
linked_data: profile.metadata.linked_data.and_then(|x| {
|
|
if let Some(project_id) = x.project_id
|
|
&& let Some(version_id) = x.version_id
|
|
&& let Some(locked) = x.locked
|
|
{
|
|
return Some(LinkedData {
|
|
project_id,
|
|
version_id,
|
|
locked,
|
|
});
|
|
}
|
|
|
|
None
|
|
}),
|
|
created: profile.metadata.date_created,
|
|
modified: profile.metadata.date_modified,
|
|
last_played: profile.metadata.last_played,
|
|
submitted_time_played: profile
|
|
.metadata
|
|
.submitted_time_played,
|
|
recent_time_played: profile.metadata.recent_time_played,
|
|
java_path: profile.java.as_ref().and_then(|x| {
|
|
x.override_version.clone().map(|x| x.path)
|
|
}),
|
|
extra_launch_args: profile
|
|
.java
|
|
.as_ref()
|
|
.and_then(|x| x.extra_arguments.clone()),
|
|
custom_env_vars: profile
|
|
.java
|
|
.and_then(|x| x.custom_env_args),
|
|
memory: profile
|
|
.memory
|
|
.map(|x| MemorySettings { maximum: x.maximum }),
|
|
force_fullscreen: profile.fullscreen,
|
|
game_resolution: profile
|
|
.resolution
|
|
.map(|x| WindowSize(x.0, x.1)),
|
|
hooks: Hooks {
|
|
pre_launch: profile
|
|
.hooks
|
|
.as_ref()
|
|
.and_then(|x| x.pre_launch.clone()),
|
|
wrapper: profile
|
|
.hooks
|
|
.as_ref()
|
|
.and_then(|x| x.wrapper.clone()),
|
|
post_exit: profile.hooks.and_then(|x| x.post_exit),
|
|
},
|
|
}
|
|
.upsert(exec)
|
|
.await?;
|
|
}
|
|
}
|
|
|
|
CachedEntry::upsert_many(
|
|
&cached_entries
|
|
.into_iter()
|
|
.map(|x| {
|
|
let mut entry = x.get_entry();
|
|
entry.expires =
|
|
Utc::now().timestamp() - entry.type_.expiry();
|
|
entry
|
|
})
|
|
.collect::<Vec<_>>(),
|
|
exec,
|
|
)
|
|
.await?;
|
|
|
|
settings.migrated = true;
|
|
settings.update(exec).await?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, Clone)]
|
|
struct LegacySettings {
|
|
pub theme: LegacyTheme,
|
|
pub memory: LegacyMemorySettings,
|
|
#[serde(default)]
|
|
pub force_fullscreen: bool,
|
|
pub game_resolution: LegacyWindowSize,
|
|
pub custom_java_args: Vec<String>,
|
|
pub custom_env_args: Vec<(String, String)>,
|
|
pub java_globals: LegacyJavaGlobals,
|
|
pub hooks: LegacyHooks,
|
|
pub max_concurrent_downloads: usize,
|
|
pub max_concurrent_writes: usize,
|
|
pub collapsed_navigation: bool,
|
|
#[serde(default)]
|
|
pub disable_discord_rpc: bool,
|
|
#[serde(default)]
|
|
pub hide_on_process: bool,
|
|
#[serde(default)]
|
|
pub native_decorations: bool,
|
|
#[serde(default)]
|
|
pub default_page: LegacyDefaultPage,
|
|
#[serde(default)]
|
|
pub developer_mode: bool,
|
|
#[serde(default)]
|
|
pub opt_out_analytics: bool,
|
|
#[serde(default)]
|
|
pub advanced_rendering: bool,
|
|
#[serde(default)]
|
|
pub fully_onboarded: bool,
|
|
#[serde(default = "default_settings_dir")]
|
|
pub loaded_config_dir: Option<PathBuf>,
|
|
}
|
|
|
|
fn default_settings_dir() -> Option<PathBuf> {
|
|
Some(dirs::config_dir()?.join("com.modrinth.theseus"))
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, Deserialize)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum LegacyTheme {
|
|
Dark,
|
|
Light,
|
|
Oled,
|
|
}
|
|
|
|
#[derive(Deserialize, Default, Debug, Clone, Copy)]
|
|
enum LegacyDefaultPage {
|
|
#[default]
|
|
Home,
|
|
Library,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, Clone)]
|
|
struct LegacyHooks {
|
|
pub pre_launch: Option<String>,
|
|
pub wrapper: Option<String>,
|
|
pub post_exit: Option<String>,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, Clone, Copy)]
|
|
struct LegacyMemorySettings {
|
|
pub maximum: u32,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, Clone, Copy)]
|
|
struct LegacyWindowSize(pub u16, pub u16);
|
|
|
|
#[derive(Debug, Deserialize, Clone)]
|
|
struct LegacyJavaGlobals(HashMap<String, LegacyJavaVersion>);
|
|
|
|
#[derive(Debug, PartialEq, Eq, Hash, Deserialize, Clone)]
|
|
struct LegacyJavaVersion {
|
|
pub path: String,
|
|
pub version: String,
|
|
pub architecture: String,
|
|
}
|
|
|
|
#[derive(Deserialize, Clone, Debug)]
|
|
struct LegacyModrinthUser {
|
|
pub id: String,
|
|
pub username: String,
|
|
// pub name: Option<String>,
|
|
pub avatar_url: Option<String>,
|
|
pub bio: Option<String>,
|
|
pub created: DateTime<Utc>,
|
|
pub role: String,
|
|
}
|
|
|
|
#[derive(Deserialize, Clone, Debug)]
|
|
struct LegacyModrinthCredentials {
|
|
pub session: String,
|
|
pub expires_at: DateTime<Utc>,
|
|
pub user: LegacyModrinthUser,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
struct LegacyMinecraftAuthStore {
|
|
pub users: HashMap<Uuid, LegacyCredentials>,
|
|
pub token: Option<LegacySaveDeviceToken>,
|
|
pub default_user: Option<Uuid>,
|
|
}
|
|
|
|
#[derive(Deserialize, Clone, Debug)]
|
|
struct LegacyCredentials {
|
|
pub id: Uuid,
|
|
pub username: String,
|
|
pub access_token: String,
|
|
pub refresh_token: String,
|
|
pub expires: DateTime<Utc>,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
struct LegacySaveDeviceToken {
|
|
pub id: String,
|
|
pub private_key: String,
|
|
pub x: String,
|
|
pub y: String,
|
|
pub token: LegacyDeviceToken,
|
|
}
|
|
|
|
#[derive(Deserialize, Clone, Debug)]
|
|
#[serde(rename_all = "PascalCase")]
|
|
struct LegacyDeviceToken {
|
|
pub issue_instant: DateTime<Utc>,
|
|
pub not_after: DateTime<Utc>,
|
|
pub token: String,
|
|
pub display_claims: HashMap<String, serde_json::Value>,
|
|
}
|
|
|
|
#[derive(Deserialize, Clone, Debug)]
|
|
struct LegacyProfile {
|
|
#[serde(default)]
|
|
pub install_stage: LegacyProfileInstallStage,
|
|
#[serde(default)]
|
|
pub path: String,
|
|
pub metadata: LegacyProfileMetadata,
|
|
pub java: Option<LegacyJavaSettings>,
|
|
pub memory: Option<LegacyMemorySettings>,
|
|
pub resolution: Option<LegacyWindowSize>,
|
|
pub fullscreen: Option<bool>,
|
|
pub hooks: Option<LegacyHooks>,
|
|
pub projects: HashMap<String, LegacyProject>,
|
|
}
|
|
|
|
#[derive(Deserialize, Clone, Debug)]
|
|
struct LegacyProject {
|
|
pub sha512: String,
|
|
// pub disabled: bool,
|
|
pub metadata: LegacyProjectMetadata,
|
|
// pub file_name: String,
|
|
}
|
|
|
|
#[derive(Deserialize, Clone, Debug)]
|
|
#[serde(tag = "type", rename_all = "snake_case")]
|
|
enum LegacyProjectMetadata {
|
|
Modrinth {
|
|
// project: Box<LegacyModrinthProject>,
|
|
version: Box<LegacyModrinthVersion>,
|
|
members: Vec<LegacyModrinthTeamMember>,
|
|
update_version: Option<Box<LegacyModrinthVersion>>,
|
|
},
|
|
Inferred,
|
|
Unknown,
|
|
}
|
|
|
|
// #[derive(Deserialize, Clone, Debug)]
|
|
// struct LegacyModrinthProject {
|
|
// pub id: String,
|
|
// pub slug: Option<String>,
|
|
// pub project_type: String,
|
|
// pub team: String,
|
|
// pub title: String,
|
|
// pub description: String,
|
|
// pub body: String,
|
|
//
|
|
// pub published: DateTime<Utc>,
|
|
// pub updated: DateTime<Utc>,
|
|
//
|
|
// pub client_side: LegacySideType,
|
|
// pub server_side: LegacySideType,
|
|
//
|
|
// pub downloads: u32,
|
|
// pub followers: u32,
|
|
//
|
|
// pub categories: Vec<String>,
|
|
// pub additional_categories: Vec<String>,
|
|
// pub game_versions: Vec<String>,
|
|
// pub loaders: Vec<String>,
|
|
//
|
|
// pub versions: Vec<String>,
|
|
//
|
|
// pub icon_url: Option<String>,
|
|
// }
|
|
|
|
#[derive(Deserialize, Clone, Debug)]
|
|
struct LegacyModrinthVersion {
|
|
pub id: String,
|
|
pub project_id: String,
|
|
pub author_id: String,
|
|
|
|
pub featured: bool,
|
|
|
|
pub name: String,
|
|
pub version_number: String,
|
|
pub changelog: String,
|
|
pub changelog_url: Option<String>,
|
|
|
|
pub date_published: DateTime<Utc>,
|
|
pub downloads: u32,
|
|
pub version_type: String,
|
|
|
|
pub files: Vec<LegacyModrinthVersionFile>,
|
|
pub dependencies: Vec<LegacyDependency>,
|
|
pub game_versions: Vec<String>,
|
|
pub loaders: Vec<String>,
|
|
}
|
|
|
|
impl From<LegacyModrinthVersion> for Version {
|
|
fn from(value: LegacyModrinthVersion) -> Self {
|
|
Version {
|
|
id: value.id,
|
|
project_id: value.project_id,
|
|
author_id: value.author_id,
|
|
featured: value.featured,
|
|
name: value.name,
|
|
version_number: value.version_number,
|
|
changelog: value.changelog,
|
|
changelog_url: value.changelog_url,
|
|
date_published: value.date_published,
|
|
downloads: value.downloads,
|
|
version_type: value.version_type,
|
|
files: value
|
|
.files
|
|
.into_iter()
|
|
.map(|x| VersionFile {
|
|
hashes: x.hashes,
|
|
url: x.url,
|
|
filename: x.filename,
|
|
primary: x.primary,
|
|
size: x.size,
|
|
file_type: x.file_type.map(|x| match x {
|
|
LegacyFileType::RequiredResourcePack => {
|
|
FileType::RequiredResourcePack
|
|
}
|
|
LegacyFileType::OptionalResourcePack => {
|
|
FileType::OptionalResourcePack
|
|
}
|
|
LegacyFileType::Unknown => FileType::Unknown,
|
|
}),
|
|
})
|
|
.collect::<Vec<_>>(),
|
|
dependencies: value
|
|
.dependencies
|
|
.into_iter()
|
|
.map(|x| Dependency {
|
|
version_id: x.version_id,
|
|
project_id: x.project_id,
|
|
file_name: x.file_name,
|
|
dependency_type: match x.dependency_type {
|
|
LegacyDependencyType::Required => {
|
|
DependencyType::Required
|
|
}
|
|
LegacyDependencyType::Optional => {
|
|
DependencyType::Optional
|
|
}
|
|
LegacyDependencyType::Incompatible => {
|
|
DependencyType::Incompatible
|
|
}
|
|
LegacyDependencyType::Embedded => {
|
|
DependencyType::Embedded
|
|
}
|
|
},
|
|
})
|
|
.collect::<Vec<_>>(),
|
|
game_versions: value.game_versions,
|
|
loaders: value.loaders,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Clone, Debug)]
|
|
struct LegacyModrinthVersionFile {
|
|
pub hashes: HashMap<String, String>,
|
|
pub url: String,
|
|
pub filename: String,
|
|
pub primary: bool,
|
|
pub size: u32,
|
|
pub file_type: Option<LegacyFileType>,
|
|
}
|
|
|
|
#[derive(Deserialize, Clone, Debug)]
|
|
struct LegacyDependency {
|
|
pub version_id: Option<String>,
|
|
pub project_id: Option<String>,
|
|
pub file_name: Option<String>,
|
|
pub dependency_type: LegacyDependencyType,
|
|
}
|
|
|
|
#[derive(Deserialize, Clone, Debug)]
|
|
struct LegacyModrinthTeamMember {
|
|
pub team_id: String,
|
|
pub user: LegacyModrinthUser,
|
|
pub role: String,
|
|
pub ordering: i64,
|
|
}
|
|
|
|
#[derive(Deserialize, Copy, Clone, Debug)]
|
|
#[serde(rename_all = "lowercase")]
|
|
enum LegacyDependencyType {
|
|
Required,
|
|
Optional,
|
|
Incompatible,
|
|
Embedded,
|
|
}
|
|
|
|
// #[derive(Deserialize, Clone, Debug, Eq, PartialEq)]
|
|
// #[serde(rename_all = "kebab-case")]
|
|
// enum LegacySideType {
|
|
// Required,
|
|
// Optional,
|
|
// Unsupported,
|
|
// Unknown,
|
|
// }
|
|
|
|
#[derive(Deserialize, Copy, Clone, Debug)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
enum LegacyFileType {
|
|
RequiredResourcePack,
|
|
OptionalResourcePack,
|
|
Unknown,
|
|
}
|
|
|
|
#[derive(Deserialize, Clone, Debug)]
|
|
struct LegacyProfileMetadata {
|
|
pub name: String,
|
|
pub icon: Option<String>,
|
|
#[serde(default)]
|
|
pub groups: Vec<String>,
|
|
|
|
pub game_version: String,
|
|
#[serde(default)]
|
|
pub loader: LegacyModLoader,
|
|
pub loader_version: Option<LegacyLoaderVersion>,
|
|
|
|
pub linked_data: Option<LegacyLinkedData>,
|
|
|
|
#[serde(default)]
|
|
pub date_created: DateTime<Utc>,
|
|
#[serde(default)]
|
|
pub date_modified: DateTime<Utc>,
|
|
pub last_played: Option<DateTime<Utc>>,
|
|
#[serde(default)]
|
|
pub submitted_time_played: u64,
|
|
#[serde(default)]
|
|
pub recent_time_played: u64,
|
|
}
|
|
|
|
#[derive(Debug, Eq, PartialEq, Clone, Copy, Deserialize, Default)]
|
|
#[serde(rename_all = "lowercase")]
|
|
enum LegacyModLoader {
|
|
#[default]
|
|
Vanilla,
|
|
Forge,
|
|
Fabric,
|
|
Quilt,
|
|
NeoForge,
|
|
}
|
|
|
|
impl From<LegacyModLoader> for ModLoader {
|
|
fn from(value: LegacyModLoader) -> Self {
|
|
match value {
|
|
LegacyModLoader::Vanilla => ModLoader::Vanilla,
|
|
LegacyModLoader::Forge => ModLoader::Forge,
|
|
LegacyModLoader::Fabric => ModLoader::Fabric,
|
|
LegacyModLoader::Quilt => ModLoader::Quilt,
|
|
LegacyModLoader::NeoForge => ModLoader::NeoForge,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Clone, Debug)]
|
|
struct LegacyLinkedData {
|
|
pub project_id: Option<String>,
|
|
pub version_id: Option<String>,
|
|
|
|
#[serde(default = "default_locked")]
|
|
pub locked: Option<bool>,
|
|
}
|
|
|
|
fn default_locked() -> Option<bool> {
|
|
Some(true)
|
|
}
|
|
|
|
#[derive(Deserialize, Clone, Debug)]
|
|
struct LegacyJavaSettings {
|
|
pub override_version: Option<LegacyJavaVersion>,
|
|
pub extra_arguments: Option<Vec<String>>,
|
|
pub custom_env_args: Option<Vec<(String, String)>>,
|
|
}
|
|
|
|
#[derive(Deserialize, Clone, Debug)]
|
|
struct LegacyLoaderVersion {
|
|
pub id: String,
|
|
}
|
|
|
|
#[derive(Deserialize, Clone, Copy, Debug, Default, Eq, PartialEq)]
|
|
#[serde(rename_all = "snake_case")]
|
|
enum LegacyProfileInstallStage {
|
|
Installed,
|
|
Installing,
|
|
PackInstalling,
|
|
#[default]
|
|
NotInstalled,
|
|
}
|