diff --git a/Cargo.lock b/Cargo.lock index d3bb39e1..9b63c728 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4557,6 +4557,7 @@ dependencies = [ "interfrost", "serde", "strum", + "sysinfo", "thiserror 2.0.18", ] diff --git a/packages/oneclient_app/src/components/memory_field.rs b/packages/oneclient_app/src/components/memory_field.rs index 76176220..5827501f 100644 --- a/packages/oneclient_app/src/components/memory_field.rs +++ b/packages/oneclient_app/src/components/memory_field.rs @@ -39,7 +39,7 @@ pub fn memory_field(mut memory: State) -> impl IntoElement { .child( TextInput::new(memory) .width(Size::px(90.)) - .placeholder("4096") + .placeholder(oneclient_common::default_mem_max().to_string()) .on_validate(validate_memory) .trailing( label() diff --git a/packages/oneclient_app/src/utils.rs b/packages/oneclient_app/src/utils.rs index e8cf54d2..8c089e06 100644 --- a/packages/oneclient_app/src/utils.rs +++ b/packages/oneclient_app/src/utils.rs @@ -5,7 +5,9 @@ use chrono::{Datelike, NaiveDate}; use oneclient_core::clusters::Cluster; use oneclient_common::domain::GameLoader; use oneclient_common::{ParsedMcVersion, VersionKey, format_mc_version, parse_mc_version}; -use sysinfo::{MemoryRefreshKind, RefreshKind, System}; + +use oneclient_common::MEMORY_HEADROOM_GB; +pub use oneclient_common::total_ram_mb; pub type ClusterGroups = BTreeMap>; @@ -207,20 +209,8 @@ pub fn format_res((w, h): (u32, u32)) -> String { /// Prevents user from choosing his max amount of ram preset (e.g Someone has 16GB of RAM, /// so the max preset is 16GB - 2GB = 14GB) -const MEMORY_HEADROOM_GB: u32 = 2; const MEMORY_PRESETS_GB: [u32; 10] = [2, 4, 6, 8, 12, 16, 24, 32, 48, 64]; -pub fn total_ram_mb() -> u32 { - static TOTAL_RAM_MB: std::sync::OnceLock = std::sync::OnceLock::new(); - - *TOTAL_RAM_MB.get_or_init(|| { - let system = System::new_with_specifics( - RefreshKind::nothing().with_memory(MemoryRefreshKind::nothing().with_ram()), - ); - (system.total_memory() / 1024 / 1024).min(u32::MAX as u64) as u32 - }) -} - pub fn memory_presets_mb() -> Vec { presets_for_total_gb((total_ram_mb() as f32 / 1024.).round() as u32) } diff --git a/packages/oneclient_app/src/view/app/cluster/cluster_settings.rs b/packages/oneclient_app/src/view/app/cluster/cluster_settings.rs index 17381e31..7242eb53 100644 --- a/packages/oneclient_app/src/view/app/cluster/cluster_settings.rs +++ b/packages/oneclient_app/src/view/app/cluster/cluster_settings.rs @@ -112,7 +112,9 @@ impl Component for ClusterSettings { MemoryRow { cluster_id, value: profile.mem_max, - global: global.mem_max.unwrap_or(4096), + global: global + .mem_max + .unwrap_or_else(oneclient_common::default_mem_max), } .into_element(), ) diff --git a/packages/oneclient_cluster/src/profile.rs b/packages/oneclient_cluster/src/profile.rs index a23c69e4..d301351d 100644 --- a/packages/oneclient_cluster/src/profile.rs +++ b/packages/oneclient_cluster/src/profile.rs @@ -58,7 +58,7 @@ impl GameSettingsProfile { java_path: None, resolution: None, force_fullscreen: Some(false), - mem_max: Some(4096), + mem_max: Some(oneclient_common::default_mem_max()), launch_args: None, launch_env: None, hook_pre: None, diff --git a/packages/oneclient_common/Cargo.toml b/packages/oneclient_common/Cargo.toml index 7ba3aa7d..6636c6ef 100644 --- a/packages/oneclient_common/Cargo.toml +++ b/packages/oneclient_common/Cargo.toml @@ -20,4 +20,5 @@ interfrost.workspace = true directories.workspace = true serde.workspace = true strum.workspace = true +sysinfo.workspace = true thiserror.workspace = true diff --git a/packages/oneclient_common/src/lib.rs b/packages/oneclient_common/src/lib.rs index bd0372b7..8f13fb87 100644 --- a/packages/oneclient_common/src/lib.rs +++ b/packages/oneclient_common/src/lib.rs @@ -3,6 +3,7 @@ pub mod constants; pub mod domain; +pub mod memory; pub mod os_ext; pub mod paths; pub mod patch; @@ -15,6 +16,7 @@ pub use domain::{ ContentType, GameLoader, HashAlgorithm, PackageUpdateMode, ProviderId, Resolution, }; pub use error::{PathsError, PathsResult}; +pub use memory::{MEMORY_HEADROOM_GB, default_mem_max, default_mem_max_for_total, total_ram_mb}; pub use os_ext::OsExt; pub use patch::Patch; pub use search::{MatchScore, SearchQuery, normalize_query}; diff --git a/packages/oneclient_common/src/memory.rs b/packages/oneclient_common/src/memory.rs new file mode 100644 index 00000000..f834c2d6 --- /dev/null +++ b/packages/oneclient_common/src/memory.rs @@ -0,0 +1,55 @@ +use sysinfo::{MemoryRefreshKind, RefreshKind, System}; + +pub const MEMORY_HEADROOM_GB: u32 = 2; + +const EIGHT_GB_MB: u32 = 7 * 1024; +const TWELVE_GB_MB: u32 = 11 * 1024; + +pub fn total_ram_mb() -> u32 { + static TOTAL_RAM_MB: std::sync::OnceLock = std::sync::OnceLock::new(); + + *TOTAL_RAM_MB.get_or_init(|| { + let system = System::new_with_specifics( + RefreshKind::nothing().with_memory(MemoryRefreshKind::nothing().with_ram()), + ); + (system.total_memory() / 1024 / 1024).min(u32::MAX as u64) as u32 + }) +} + +#[must_use] +pub fn default_mem_max() -> u32 { + default_mem_max_for_total(total_ram_mb()) +} + +#[must_use] +pub fn default_mem_max_for_total(total_mb: u32) -> u32 { + match total_mb { + 0..EIGHT_GB_MB => 2048, + EIGHT_GB_MB..TWELVE_GB_MB => 3072, + _ => 4096, + } +} + +#[cfg(test)] +mod tests { + use super::default_mem_max_for_total; + + #[test] + fn the_default_heap_ramps_with_total_ram() { + assert_eq!(default_mem_max_for_total(3987), 2048); // 4GB + assert_eq!(default_mem_max_for_total(6060), 2048); // 6GB + assert_eq!(default_mem_max_for_total(7167), 2048); + assert_eq!(default_mem_max_for_total(7168), 3072); + assert_eq!(default_mem_max_for_total(7900), 3072); // 8GB + assert_eq!(default_mem_max_for_total(11263), 3072); + assert_eq!(default_mem_max_for_total(11264), 4096); + assert_eq!(default_mem_max_for_total(11800), 4096); // 12GB + assert_eq!(default_mem_max_for_total(16290), 4096); // 16GB + assert_eq!(default_mem_max_for_total(65229), 4096); // 64GB + } + + #[test] + fn a_machine_that_reports_nothing_still_gets_a_heap() { + assert_eq!(default_mem_max_for_total(0), 2048); + } +} diff --git a/packages/oneclient_core/src/game/launch.rs b/packages/oneclient_core/src/game/launch.rs index 0d42e1fd..fe6efc7d 100644 --- a/packages/oneclient_core/src/game/launch.rs +++ b/packages/oneclient_core/src/game/launch.rs @@ -259,7 +259,7 @@ pub async fn launch_cluster( &libraries, &classpaths, &version_name, - profile.mem_max.unwrap_or(2048), + profile.mem_max.unwrap_or_else(oneclient_common::default_mem_max), profile.launch_args.clone().unwrap_or_default(), &java.os_arch, java.major, @@ -344,8 +344,13 @@ pub async fn launch_cluster( mc_version: cluster.mc_version.clone(), }); - let recorder = - SessionRecorder::start(state, cluster_id, profile.mem_max.unwrap_or(2048), &java).await; + let recorder = SessionRecorder::start( + state, + cluster_id, + profile.mem_max.unwrap_or_else(oneclient_common::default_mem_max), + &java, + ) + .await; // Pinned to the session row so that if the launcher exits first the next // start can tell whether the game is still playing diff --git a/packages/oneclient_core/src/settings/launcher.rs b/packages/oneclient_core/src/settings/launcher.rs index 77119c86..3cd730e4 100644 --- a/packages/oneclient_core/src/settings/launcher.rs +++ b/packages/oneclient_core/src/settings/launcher.rs @@ -20,6 +20,8 @@ pub struct ViewState { pub sort: Option, } +pub const SETTINGS_VERSION: u32 = 2; + #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(default)] pub struct LauncherSettings { @@ -59,7 +61,7 @@ impl LauncherSettings { impl Default for LauncherSettings { fn default() -> Self { Self { - settings_version: 1, + settings_version: SETTINGS_VERSION, log_debug: false, auto_update: true, crash_reporting: true, diff --git a/packages/oneclient_core/src/settings/store.rs b/packages/oneclient_core/src/settings/store.rs index 4ca91f32..6b285cef 100644 --- a/packages/oneclient_core/src/settings/store.rs +++ b/packages/oneclient_core/src/settings/store.rs @@ -2,9 +2,10 @@ use parking_lot::RwLock; use oneclient_events::EventBus; use oneclient_common::paths; +use oneclient_db::DbPool; use crate::{LauncherError, LauncherResult}; -use super::launcher::LauncherSettings; +use super::launcher::{LauncherSettings, SETTINGS_VERSION}; use oneclient_cluster::GameSettingsProfile; #[tracing::instrument(level = "debug", skip(notify))] @@ -35,6 +36,39 @@ pub async fn load_settings(notify: Option<&EventBus>) -> LauncherSettings { } } +const LEGACY_MEM_MAX: u32 = 4096; + +#[tracing::instrument(level = "debug", skip_all)] +pub async fn migrate(pool: &DbPool, settings: &mut LauncherSettings) -> LauncherResult<()> { + if !migrate_settings(settings) { + return Ok(()); + } + + let mem_max = oneclient_common::default_mem_max(); + if mem_max != LEGACY_MEM_MAX { + let profiles = + oneclient_db::dao::setting_profile::replace_mem_max(pool, LEGACY_MEM_MAX, mem_max) + .await?; + + tracing::info!("lowered the default heap to {mem_max}MB on {profiles} cluster profiles"); + } + + save_settings(settings).await +} + +fn migrate_settings(settings: &mut LauncherSettings) -> bool { + if settings.settings_version >= SETTINGS_VERSION { + return false; + } + + if settings.global_game_settings.mem_max == Some(LEGACY_MEM_MAX) { + settings.global_game_settings.mem_max = Some(oneclient_common::default_mem_max()); + } + + settings.settings_version = SETTINGS_VERSION; + true +} + /// Prefer [`save_settings_and_apply`] this leaves the HTTP client on its old /// endpoints/keys so those changes only take effect on the next launch #[tracing::instrument(level = "debug", skip(settings))] @@ -69,3 +103,48 @@ pub async fn save_global_profile( let snapshot = settings.read().clone(); save_settings(&snapshot).await } + +#[cfg(test)] +mod tests { + use super::{GameSettingsProfile, LauncherSettings, SETTINGS_VERSION, migrate_settings}; + + fn v1(mem_max: Option) -> LauncherSettings { + LauncherSettings { + settings_version: 1, + global_game_settings: GameSettingsProfile { + mem_max, + ..LauncherSettings::default().global_game_settings + }, + ..LauncherSettings::default() + } + } + + #[test] + fn the_old_hardcoded_heap_becomes_the_per_machine_default() { + let mut settings = v1(Some(4096)); + migrate_settings(&mut settings); + + assert_eq!( + settings.global_game_settings.mem_max, + Some(oneclient_common::default_mem_max()) + ); + assert_eq!(settings.settings_version, SETTINGS_VERSION); + } + + #[test] + fn a_heap_the_user_chose_survives() { + let mut settings = v1(Some(8192)); + migrate_settings(&mut settings); + + assert_eq!(settings.global_game_settings.mem_max, Some(8192)); + } + + #[test] + fn an_already_migrated_file_is_left_alone() { + let mut settings = v1(Some(4096)); + settings.settings_version = SETTINGS_VERSION; + migrate_settings(&mut settings); + + assert_eq!(settings.global_game_settings.mem_max, Some(4096)); + } +} diff --git a/packages/oneclient_core/src/state.rs b/packages/oneclient_core/src/state.rs index 7bda2d29..095a4462 100644 --- a/packages/oneclient_core/src/state.rs +++ b/packages/oneclient_core/src/state.rs @@ -72,7 +72,11 @@ impl LauncherState { packages: PackageProviderRegistry::new(), }; - let settings = store::load_settings(Some(&services.events)).await; + let mut settings = store::load_settings(Some(&services.events)).await; + if let Err(err) = store::migrate(&services.db, &mut settings).await { + tracing::error!("settings migration failed, retrying next start: {err}"); + } + services .requester .set_config(crate::settings::net_config(&settings)); diff --git a/packages/oneclient_core/tests/clusters.rs b/packages/oneclient_core/tests/clusters.rs index 3ab6afd1..f630fdaa 100644 --- a/packages/oneclient_core/tests/clusters.rs +++ b/packages/oneclient_core/tests/clusters.rs @@ -13,11 +13,9 @@ async fn cluster_lifecycle_with_settings_profile() { let global = state.settings.read().global_game_settings.clone(); - // Deliberately not the global default of 4096 so the assertions distinguish - // "the cluster's own profile was used" from "fell back to global and matched" let cluster = state.clusters.create( &global, - CreateClusterOptions::new("Test Cluster", "1.21.1", GameLoader::Fabric).mem_max(2048), + CreateClusterOptions::new("Test Cluster", "1.21.1", GameLoader::Fabric).mem_max(3072), ) .await .unwrap(); @@ -31,7 +29,7 @@ async fn cluster_lifecycle_with_settings_profile() { let resolved = state.clusters.resolve_settings(&global, &cluster) .await .unwrap(); - assert_eq!(resolved.mem_max, Some(2048)); + assert_eq!(resolved.mem_max, Some(3072)); state.clusters.update_profile(cluster.id, ProfileUpdate { diff --git a/packages/oneclient_db/.sqlx/query-0d9fb5d2fa8a788770e946241ea2f62f4b794c0fdda3ccbde49384689911aeba.json b/packages/oneclient_db/.sqlx/query-0c3651cf175cb900c2466f0e97beca338b95637d43d9d067b974327f7720637b.json similarity index 77% rename from packages/oneclient_db/.sqlx/query-0d9fb5d2fa8a788770e946241ea2f62f4b794c0fdda3ccbde49384689911aeba.json rename to packages/oneclient_db/.sqlx/query-0c3651cf175cb900c2466f0e97beca338b95637d43d9d067b974327f7720637b.json index 5da3a9d7..3ce34f5c 100644 --- a/packages/oneclient_db/.sqlx/query-0d9fb5d2fa8a788770e946241ea2f62f4b794c0fdda3ccbde49384689911aeba.json +++ b/packages/oneclient_db/.sqlx/query-0c3651cf175cb900c2466f0e97beca338b95637d43d9d067b974327f7720637b.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "\n SELECT absolute_path, major, version, vendor, os_arch, is_jdk as \"is_jdk: bool\"\n FROM java_versions\n ORDER BY major DESC, version DESC\n ", + "query": "\n SELECT absolute_path, major, version, vendor, os_arch, is_jdk as \"is_jdk: bool\", probe_version\n FROM java_versions\n ORDER BY major DESC, version DESC\n ", "describe": { "columns": [ { @@ -68,6 +68,17 @@ "name": "is_jdk" } } + }, + { + "name": "probe_version", + "ordinal": 6, + "type_info": "Integer", + "origin": { + "Table": { + "table": "java_versions", + "name": "probe_version" + } + } } ], "parameters": { @@ -79,8 +90,9 @@ false, false, false, + false, false ] }, - "hash": "0d9fb5d2fa8a788770e946241ea2f62f4b794c0fdda3ccbde49384689911aeba" + "hash": "0c3651cf175cb900c2466f0e97beca338b95637d43d9d067b974327f7720637b" } diff --git a/packages/oneclient_db/.sqlx/query-33771b7003e64c8f19d15d663de773afa568d626c60a37bbc32cc932746ad49b.json b/packages/oneclient_db/.sqlx/query-14ab205c5b8d4c1c435814e4b7a155acbae41a67925b6b179b82dea1758d621a.json similarity index 77% rename from packages/oneclient_db/.sqlx/query-33771b7003e64c8f19d15d663de773afa568d626c60a37bbc32cc932746ad49b.json rename to packages/oneclient_db/.sqlx/query-14ab205c5b8d4c1c435814e4b7a155acbae41a67925b6b179b82dea1758d621a.json index caaad113..e50e191f 100644 --- a/packages/oneclient_db/.sqlx/query-33771b7003e64c8f19d15d663de773afa568d626c60a37bbc32cc932746ad49b.json +++ b/packages/oneclient_db/.sqlx/query-14ab205c5b8d4c1c435814e4b7a155acbae41a67925b6b179b82dea1758d621a.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "\n\t\tSELECT absolute_path, major, version, vendor, os_arch, is_jdk as \"is_jdk: bool\"\n\t\tFROM java_versions\n\t\tWHERE major = ?\n\t\tORDER BY version DESC\n\t\tLIMIT 1\n\t\t", + "query": "\n\t\tSELECT absolute_path, major, version, vendor, os_arch, is_jdk as \"is_jdk: bool\", probe_version\n\t\tFROM java_versions\n\t\tWHERE major = ?\n\t\tORDER BY version DESC\n\t\tLIMIT 1\n\t\t", "describe": { "columns": [ { @@ -68,6 +68,17 @@ "name": "is_jdk" } } + }, + { + "name": "probe_version", + "ordinal": 6, + "type_info": "Integer", + "origin": { + "Table": { + "table": "java_versions", + "name": "probe_version" + } + } } ], "parameters": { @@ -79,8 +90,9 @@ false, false, false, + false, false ] }, - "hash": "33771b7003e64c8f19d15d663de773afa568d626c60a37bbc32cc932746ad49b" + "hash": "14ab205c5b8d4c1c435814e4b7a155acbae41a67925b6b179b82dea1758d621a" } diff --git a/packages/oneclient_db/.sqlx/query-f5992c9cec6a6d7015cda4c04b463bd5fd9c42ac38b434fa8a0c0fe4f7517cf2.json b/packages/oneclient_db/.sqlx/query-331bee3da454f39a8a2cc8ed4e5338b743a7cd033fdef023c4b3b93827248be1.json similarity index 78% rename from packages/oneclient_db/.sqlx/query-f5992c9cec6a6d7015cda4c04b463bd5fd9c42ac38b434fa8a0c0fe4f7517cf2.json rename to packages/oneclient_db/.sqlx/query-331bee3da454f39a8a2cc8ed4e5338b743a7cd033fdef023c4b3b93827248be1.json index 27530fbf..23c27b31 100644 --- a/packages/oneclient_db/.sqlx/query-f5992c9cec6a6d7015cda4c04b463bd5fd9c42ac38b434fa8a0c0fe4f7517cf2.json +++ b/packages/oneclient_db/.sqlx/query-331bee3da454f39a8a2cc8ed4e5338b743a7cd033fdef023c4b3b93827248be1.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "\n SELECT absolute_path, major, version, vendor, os_arch, is_jdk as \"is_jdk: bool\"\n FROM java_versions\n WHERE absolute_path = ?\n ", + "query": "\n SELECT absolute_path, major, version, vendor, os_arch, is_jdk as \"is_jdk: bool\", probe_version\n FROM java_versions\n WHERE absolute_path = ?\n ", "describe": { "columns": [ { @@ -68,6 +68,17 @@ "name": "is_jdk" } } + }, + { + "name": "probe_version", + "ordinal": 6, + "type_info": "Integer", + "origin": { + "Table": { + "table": "java_versions", + "name": "probe_version" + } + } } ], "parameters": { @@ -79,8 +90,9 @@ false, false, false, + false, false ] }, - "hash": "f5992c9cec6a6d7015cda4c04b463bd5fd9c42ac38b434fa8a0c0fe4f7517cf2" + "hash": "331bee3da454f39a8a2cc8ed4e5338b743a7cd033fdef023c4b3b93827248be1" } diff --git a/packages/oneclient_db/.sqlx/query-756a83e9d46e8f0450c172efb35f6fa42554910d9b7e7c09c4a593c96dec3b2a.json b/packages/oneclient_db/.sqlx/query-756a83e9d46e8f0450c172efb35f6fa42554910d9b7e7c09c4a593c96dec3b2a.json new file mode 100644 index 00000000..96e78da3 --- /dev/null +++ b/packages/oneclient_db/.sqlx/query-756a83e9d46e8f0450c172efb35f6fa42554910d9b7e7c09c4a593c96dec3b2a.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n\t\tUPDATE setting_profiles\n\t\tSET mem_max = ?\n\t\tWHERE mem_max = ?\n\t\t", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "756a83e9d46e8f0450c172efb35f6fa42554910d9b7e7c09c4a593c96dec3b2a" +} diff --git a/packages/oneclient_db/.sqlx/query-010f5dcda59ec32eaad1b9c3e2e9807f547a1376372c8b201653a36d6dd14cbc.json b/packages/oneclient_db/.sqlx/query-f5dbcf7798afb495efe228a4ba7d1ddd9904cc25f34bef3c16f29cfb141158f0.json similarity index 66% rename from packages/oneclient_db/.sqlx/query-010f5dcda59ec32eaad1b9c3e2e9807f547a1376372c8b201653a36d6dd14cbc.json rename to packages/oneclient_db/.sqlx/query-f5dbcf7798afb495efe228a4ba7d1ddd9904cc25f34bef3c16f29cfb141158f0.json index 33514fa6..044b3eb0 100644 --- a/packages/oneclient_db/.sqlx/query-010f5dcda59ec32eaad1b9c3e2e9807f547a1376372c8b201653a36d6dd14cbc.json +++ b/packages/oneclient_db/.sqlx/query-f5dbcf7798afb495efe228a4ba7d1ddd9904cc25f34bef3c16f29cfb141158f0.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "\n\t\tINSERT INTO java_versions (absolute_path, major, version, vendor, os_arch, is_jdk)\n\t\tVALUES (?, ?, ?, ?, ?, ?)\n\t\tON CONFLICT(absolute_path) DO UPDATE SET\n\t\t\tmajor = excluded.major,\n\t\t\tversion = excluded.version,\n\t\t\tvendor = excluded.vendor,\n\t\t\tos_arch = excluded.os_arch,\n\t\t\tis_jdk = excluded.is_jdk\n\t\tRETURNING absolute_path, major, version, vendor, os_arch, is_jdk as \"is_jdk: bool\"\n\t\t", + "query": "\n\t\tINSERT INTO java_versions (absolute_path, major, version, vendor, os_arch, is_jdk, probe_version)\n\t\tVALUES (?, ?, ?, ?, ?, ?, ?)\n\t\tON CONFLICT(absolute_path) DO UPDATE SET\n\t\t\tmajor = excluded.major,\n\t\t\tversion = excluded.version,\n\t\t\tvendor = excluded.vendor,\n\t\t\tos_arch = excluded.os_arch,\n\t\t\tis_jdk = excluded.is_jdk,\n\t\t\tprobe_version = excluded.probe_version\n\t\tRETURNING absolute_path, major, version, vendor, os_arch, is_jdk as \"is_jdk: bool\", probe_version\n\t\t", "describe": { "columns": [ { @@ -68,10 +68,21 @@ "name": "is_jdk" } } + }, + { + "name": "probe_version", + "ordinal": 6, + "type_info": "Integer", + "origin": { + "Table": { + "table": "java_versions", + "name": "probe_version" + } + } } ], "parameters": { - "Right": 6 + "Right": 7 }, "nullable": [ false, @@ -79,8 +90,9 @@ false, false, false, + false, false ] }, - "hash": "010f5dcda59ec32eaad1b9c3e2e9807f547a1376372c8b201653a36d6dd14cbc" + "hash": "f5dbcf7798afb495efe228a4ba7d1ddd9904cc25f34bef3c16f29cfb141158f0" } diff --git a/packages/oneclient_db/src/dao/setting_profile.rs b/packages/oneclient_db/src/dao/setting_profile.rs index 0511ece1..4541a540 100644 --- a/packages/oneclient_db/src/dao/setting_profile.rs +++ b/packages/oneclient_db/src/dao/setting_profile.rs @@ -43,6 +43,24 @@ pub async fn get_by_name(pool: &SqlitePool, name: &str) -> DbResult DbResult { + let (from, to) = (i64::from(from), i64::from(to)); + + let result = sqlx::query!( + r#" + UPDATE setting_profiles + SET mem_max = ? + WHERE mem_max = ? + "#, + to, + from, + ) + .execute(pool) + .await?; + + Ok(result.rows_affected()) +} + pub async fn upsert( pool: &SqlitePool, row: &SettingProfileRow, @@ -106,3 +124,60 @@ pub async fn delete_by_name(pool: &SqlitePool, name: &str) -> Result<(), DbError Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + + async fn pool() -> SqlitePool { + let pool = SqlitePool::connect("sqlite::memory:") + .await + .expect("in-memory sqlite"); + sqlx::migrate!().run(&pool).await.expect("migrations run"); + pool + } + + async fn seed(pool: &SqlitePool, name: &str, mem_max: Option) { + upsert( + pool, + &SettingProfileRow { + name: name.into(), + java_path: None, + resolution: None, + force_fullscreen: None, + mem_max, + launch_args: None, + launch_env: None, + hook_pre: None, + hook_wrapper: None, + hook_post: None, + os_extra: None, + browser_update_mode: None, + }, + ) + .await + .expect("insert profile"); + } + + #[tokio::test] + async fn only_the_legacy_heap_is_replaced() { + let pool = pool().await; + seed(&pool, "stale", Some(4096)).await; + seed(&pool, "also stale", Some(4096)).await; + seed(&pool, "chosen", Some(8192)).await; + seed(&pool, "inherits", None).await; + + let replaced = replace_mem_max(&pool, 4096, 3072).await.expect("replace"); + assert_eq!(replaced, 2); + + let mem_max = |name: &'static str| { + let pool = pool.clone(); + async move { get_by_name(&pool, name).await.unwrap().unwrap().mem_max } + }; + + assert_eq!(mem_max("stale").await, Some(3072)); + assert_eq!(mem_max("also stale").await, Some(3072)); + assert_eq!(mem_max("chosen").await, Some(8192)); + assert_eq!(mem_max("inherits").await, None); + } +}