pub mod data { use std::{hash::Hash, path::PathBuf}; use native_model::native_model; use serde::{Deserialize, Serialize}; // NOTE: Within each version, you should NEVER use these types. // Declare it using the actual version that it is from, i.e. v1::Settings rather than just Settings from here pub type GameVersion = v1::GameVersion; pub type Database = v3::Database; pub type Settings = v1::Settings; pub type DatabaseAuth = v1::DatabaseAuth; pub type GameDownloadStatus = v2::GameDownloadStatus; pub type ApplicationTransientStatus = v1::ApplicationTransientStatus; /** * Need to be universally accessible by the ID, and the version is just a couple sprinkles on top */ pub type DownloadableMetadata = v1::DownloadableMetadata; pub type DownloadType = v1::DownloadType; pub type DatabaseApplications = v2::DatabaseApplications; // pub type DatabaseCompatInfo = v2::DatabaseCompatInfo; use std::collections::HashMap; impl PartialEq for DownloadableMetadata { fn eq(&self, other: &Self) -> bool { self.id == other.id && self.download_type == other.download_type } } impl Hash for DownloadableMetadata { fn hash(&self, state: &mut H) { self.id.hash(state); self.download_type.hash(state); } } mod v1 { use crate::process::process_manager::Platform; use serde_with::serde_as; use std::{collections::HashMap, path::PathBuf}; use super::{Deserialize, Serialize, native_model}; fn default_template() -> String { "{}".to_owned() } #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(rename_all = "camelCase")] #[native_model(id = 2, version = 1, with = native_model::rmp_serde_1_3::RmpSerde)] pub struct GameVersion { pub game_id: String, pub version_name: String, pub platform: Platform, pub launch_command: String, pub launch_args: Vec, #[serde(default = "default_template")] pub launch_command_template: String, pub setup_command: String, pub setup_args: Vec, #[serde(default = "default_template")] pub setup_command_template: String, pub only_setup: bool, pub version_index: usize, pub delta: bool, pub umu_id_override: Option, } #[serde_as] #[derive(Serialize, Clone, Deserialize, Default)] #[serde(rename_all = "camelCase")] #[native_model(id = 3, version = 1, with = native_model::rmp_serde_1_3::RmpSerde)] pub struct DatabaseApplications { pub install_dirs: Vec, // Guaranteed to exist if the game also exists in the app state map pub game_statuses: HashMap, pub game_versions: HashMap>, pub installed_game_version: HashMap, #[serde(skip)] pub transient_statuses: HashMap, } #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(rename_all = "camelCase")] #[native_model(id = 4, version = 1, with = native_model::rmp_serde_1_3::RmpSerde)] pub struct Settings { pub autostart: bool, pub max_download_threads: usize, pub force_offline: bool, // ... other settings ... } impl Default for Settings { fn default() -> Self { Self { autostart: false, max_download_threads: 4, force_offline: false, } } } // Strings are version names for a particular game #[derive(Serialize, Clone, Deserialize)] #[serde(tag = "type")] #[native_model(id = 5, version = 1, with = native_model::rmp_serde_1_3::RmpSerde)] pub enum GameDownloadStatus { Remote {}, SetupRequired { version_name: String, install_dir: String, }, Installed { version_name: String, install_dir: String, }, } // Stuff that shouldn't be synced to disk #[derive(Clone, Serialize, Deserialize, Debug)] pub enum ApplicationTransientStatus { Queued { version_name: String }, Downloading { version_name: String }, Uninstalling {}, Updating { version_name: String }, Validating { version_name: String }, Running {}, } #[derive(serde::Serialize, Clone, Deserialize)] #[native_model(id = 6, version = 1, with = native_model::rmp_serde_1_3::RmpSerde)] pub struct DatabaseAuth { pub private: String, pub cert: String, pub client_id: String, pub web_token: Option, } #[native_model(id = 8, version = 1)] #[derive( Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Clone, Copy, )] pub enum DownloadType { Game, Tool, Dlc, Mod, } #[native_model(id = 7, version = 1, with = native_model::rmp_serde_1_3::RmpSerde)] #[derive(Debug, Eq, PartialOrd, Ord, Serialize, Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct DownloadableMetadata { pub id: String, pub version: Option, pub download_type: DownloadType, } impl DownloadableMetadata { pub fn new(id: String, version: Option, download_type: DownloadType) -> Self { Self { id, version, download_type, } } } #[native_model(id = 1, version = 1)] #[derive(Serialize, Deserialize, Clone, Default)] pub struct Database { #[serde(default)] pub settings: Settings, pub auth: Option, pub base_url: String, pub applications: DatabaseApplications, pub prev_database: Option, pub cache_dir: PathBuf, } } mod v2 { use std::{collections::HashMap, path::PathBuf}; use serde_with::serde_as; use super::{ Deserialize, Serialize, native_model, v1, }; #[native_model(id = 1, version = 2, with = native_model::rmp_serde_1_3::RmpSerde, from = v1::Database)] #[derive(Serialize, Deserialize, Clone, Default)] pub struct Database { #[serde(default)] pub settings: v1::Settings, pub auth: Option, pub base_url: String, pub applications: v1::DatabaseApplications, #[serde(skip)] pub prev_database: Option, pub cache_dir: PathBuf, pub compat_info: Option, } #[native_model(id = 9, version = 1, with = native_model::rmp_serde_1_3::RmpSerde)] #[derive(Serialize, Deserialize, Clone, Default)] pub struct DatabaseCompatInfo { pub umu_installed: bool, } impl From for Database { fn from(value: v1::Database) -> Self { Self { settings: value.settings, auth: value.auth, base_url: value.base_url, applications: value.applications, prev_database: value.prev_database, cache_dir: value.cache_dir, compat_info: None, } } } // Strings are version names for a particular game #[derive(Serialize, Clone, Deserialize, Debug)] #[serde(tag = "type")] #[native_model(id = 5, version = 2, with = native_model::rmp_serde_1_3::RmpSerde, from = v1::GameDownloadStatus)] pub enum GameDownloadStatus { Remote {}, SetupRequired { version_name: String, install_dir: String, }, Installed { version_name: String, install_dir: String, }, PartiallyInstalled { version_name: String, install_dir: String, }, } impl From for GameDownloadStatus { fn from(value: v1::GameDownloadStatus) -> Self { match value { v1::GameDownloadStatus::Remote {} => Self::Remote {}, v1::GameDownloadStatus::SetupRequired { version_name, install_dir, } => Self::SetupRequired { version_name, install_dir, }, v1::GameDownloadStatus::Installed { version_name, install_dir, } => Self::Installed { version_name, install_dir, }, } } } #[serde_as] #[derive(Serialize, Clone, Deserialize, Default)] #[serde(rename_all = "camelCase")] #[native_model(id = 3, version = 2, with = native_model::rmp_serde_1_3::RmpSerde, from=v1::DatabaseApplications)] pub struct DatabaseApplications { pub install_dirs: Vec, // Guaranteed to exist if the game also exists in the app state map pub game_statuses: HashMap, pub game_versions: HashMap>, pub installed_game_version: HashMap, #[serde(skip)] pub transient_statuses: HashMap, } impl From for DatabaseApplications { fn from(value: v1::DatabaseApplications) -> Self { Self { game_statuses: value .game_statuses .into_iter() .map(|x| (x.0, x.1.into())) .collect::>(), install_dirs: value.install_dirs, game_versions: value.game_versions, installed_game_version: value.installed_game_version, transient_statuses: value.transient_statuses, } } } } mod v3 { use std::path::PathBuf; use super::{ Deserialize, Serialize, native_model, v2, v1, }; #[native_model(id = 1, version = 3, with = native_model::rmp_serde_1_3::RmpSerde, from = v2::Database)] #[derive(Serialize, Deserialize, Clone, Default)] pub struct Database { #[serde(default)] pub settings: v1::Settings, pub auth: Option, pub base_url: String, pub applications: v2::DatabaseApplications, #[serde(skip)] pub prev_database: Option, pub cache_dir: PathBuf, pub compat_info: Option, } impl From for Database { fn from(value: v2::Database) -> Self { Self { settings: value.settings, auth: value.auth, base_url: value.base_url, applications: value.applications.into(), prev_database: value.prev_database, cache_dir: value.cache_dir, compat_info: None, } } } } impl Database { pub fn new>( games_base_dir: T, prev_database: Option, cache_dir: PathBuf, ) -> Self { Self { applications: DatabaseApplications { install_dirs: vec![games_base_dir.into()], game_statuses: HashMap::new(), game_versions: HashMap::new(), installed_game_version: HashMap::new(), transient_statuses: HashMap::new(), }, prev_database, base_url: String::new(), auth: None, settings: Settings::default(), cache_dir, compat_info: None, } } } }