From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Modrinth Enhanced Date: Fri, 18 Sep 2026 00:03:21 +0200 Subject: [PATCH] Offer the launchers instances can be imported from Importing only listed the launchers that were found and had instances. Anyone whose launcher sits somewhere else, or who has not installed it where the launcher usually goes, was left with an empty list and a path to type in by hand. Every launcher the importers read is offered now, installed or not: Prism Launcher, MultiMC, PolyMC, ATLauncher, GDLauncher and CurseForge, each with the folder it keeps its instances in on this system, Flatpak and macOS bundles included. Picking one reads that folder; with nothing there, its path lands in the field to be corrected. PolyMC and other MultiMC forks are read by Prism's importer, so the name shown and the importer used are no longer the same thing. The initials in a badge tell the entries apart without carrying anyone else's logo around. --- apps/app-frontend/src/helpers/import.js | 6 + .../src/providers/setup/creation-modal.ts | 7 +- .../src/providers/setup/instance-import.ts | 26 +++- apps/app/build.rs | 1 + apps/app/src/api/import.rs | 8 + packages/app-lib/src/api/pack/import/mod.rs | 146 ++++++++++++++---- packages/ui/src/assets/launchers/README.md | 16 ++ .../ui/src/assets/launchers/atlauncher.svg | 1 + .../ui/src/assets/launchers/gdlauncher.png | Bin 0 -> 7218 bytes packages/ui/src/assets/launchers/multimc.svg | 1 + packages/ui/src/assets/launchers/polymc.svg | 21 +++ .../src/assets/launchers/prism-launcher.svg | 57 +++++++ .../components/ImportInstanceStage.vue | 109 ++++++++++++- .../components/LauncherMark.vue | 55 +++++++ packages/ui/src/providers/instance-import.ts | 14 ++ 15 files changed, 426 insertions(+), 42 deletions(-) create mode 100644 packages/ui/src/assets/launchers/README.md create mode 100644 packages/ui/src/assets/launchers/atlauncher.svg create mode 100644 packages/ui/src/assets/launchers/gdlauncher.png create mode 100644 packages/ui/src/assets/launchers/multimc.svg create mode 100644 packages/ui/src/assets/launchers/polymc.svg create mode 100644 packages/ui/src/assets/launchers/prism-launcher.svg create mode 100644 packages/ui/src/components/flows/creation-flow-modal/components/LauncherMark.vue diff --git a/apps/app-frontend/src/helpers/import.js b/apps/app-frontend/src/helpers/import.js index 34cf250..bfc728e 100644 --- a/apps/app-frontend/src/helpers/import.js +++ b/apps/app-frontend/src/helpers/import.js @@ -45,6 +45,12 @@ export async function is_valid_importable_instance(instanceFolder, launcherType) }) } +/// Gets every launcher instances can be imported from, with the folder each one +/// uses on this system, whether it is installed or not +export async function launcher_suggestions() { + return await invoke('plugin:import|launcher_suggestions') +} + /// Gets the default path for the given launcher type /// null if it can't be found or doesn't exist /// eg: get_default_launcher_path("MultiMC") diff --git a/apps/app-frontend/src/providers/setup/creation-modal.ts b/apps/app-frontend/src/providers/setup/creation-modal.ts index 964b17c..53ba797 100644 --- a/apps/app-frontend/src/providers/setup/creation-modal.ts +++ b/apps/app-frontend/src/providers/setup/creation-modal.ts @@ -113,7 +113,12 @@ export function setupCreationModal( if (!launcher || instanceSet.size === 0) continue for (const name of instanceSet) { importCount += 1 - const job = await import_instance(launcher.name, launcher.path, name).catch(handleError) + // PolyMC and other forks are read by another launcher's importer. + const job = await import_instance( + launcher.launcher ?? launcher.name, + launcher.path, + name, + ).catch(handleError) if (job) { importedInstanceId = installJobInstanceId(job) } diff --git a/apps/app-frontend/src/providers/setup/instance-import.ts b/apps/app-frontend/src/providers/setup/instance-import.ts index 851e44d..752832c 100644 --- a/apps/app-frontend/src/providers/setup/instance-import.ts +++ b/apps/app-frontend/src/providers/setup/instance-import.ts @@ -2,9 +2,9 @@ import { type AbstractWebNotificationManager, provideInstanceImport } from '@mod import { open } from '@tauri-apps/plugin-dialog' import { - get_default_launcher_path, get_importable_instances, import_instance, + launcher_suggestions, } from '@/helpers/import.js' export function setupInstanceImportProvider(notificationManager: AbstractWebNotificationManager) { @@ -12,15 +12,18 @@ export function setupInstanceImportProvider(notificationManager: AbstractWebNoti provideInstanceImport({ async getDetectedLaunchers() { - const launcherNames = ['MultiMC', 'GDLauncher', 'ATLauncher', 'Curseforge', 'PrismLauncher'] const launchers = [] - for (const name of launcherNames) { + for (const suggestion of await launcher_suggestions()) { + if (!suggestion.installed || !suggestion.path) continue try { - const path = await get_default_launcher_path(name) - if (!path) continue - const instances = await get_importable_instances(name, path) + const instances = await get_importable_instances(suggestion.launcher, suggestion.path) if (instances?.length > 0) { - launchers.push({ name, path, instances }) + launchers.push({ + name: suggestion.name, + launcher: suggestion.launcher, + path: suggestion.path, + instances, + }) } } catch { // Skip launchers that fail detection @@ -28,6 +31,15 @@ export function setupInstanceImportProvider(notificationManager: AbstractWebNoti } return launchers }, + async getLauncherSuggestions() { + const suggestions = await launcher_suggestions() + return suggestions.map((suggestion) => ({ + name: suggestion.name, + launcher: suggestion.launcher, + path: suggestion.path ?? '', + installed: suggestion.installed, + })) + }, async getImportableInstances(launcherName: string, path: string) { return (await get_importable_instances(launcherName, path)) ?? [] }, diff --git a/apps/app/build.rs b/apps/app/build.rs index 8cb21f2..8e8a0f8 100644 --- a/apps/app/build.rs +++ b/apps/app/build.rs @@ -108,6 +108,7 @@ fn main() { "get_importable_instances", "is_valid_importable_instance", "get_default_launcher_path", + "launcher_suggestions", ]) .default_permission( DefaultPermissionRule::AllowAllCommands, diff --git a/apps/app/src/api/import.rs b/apps/app/src/api/import.rs index 773fcb2..cda652f 100644 --- a/apps/app/src/api/import.rs +++ b/apps/app/src/api/import.rs @@ -11,6 +11,7 @@ pub fn init() -> tauri::plugin::TauriPlugin { get_importable_instances, is_valid_importable_instance, get_default_launcher_path, + launcher_suggestions, ]) .build() } @@ -39,6 +40,13 @@ pub async fn is_valid_importable_instance( ) } +/// Returns every launcher that can be imported from, installed or not, with +/// the folder each one keeps its instances in on this system. +#[tauri::command] +pub async fn launcher_suggestions() -> Result> { + Ok(import::launcher_suggestions()) +} + /// Returns the default path for the given launcher type /// None if it can't be found or doesn't exist #[tauri::command] diff --git a/packages/app-lib/src/api/pack/import/mod.rs b/packages/app-lib/src/api/pack/import/mod.rs index 72f315d..15da6da 100644 --- a/packages/app-lib/src/api/pack/import/mod.rs +++ b/packages/app-lib/src/api/pack/import/mod.rs @@ -247,36 +247,130 @@ async fn import_instance_inner( pub fn get_default_launcher_path( r#type: ImportLauncherType, ) -> Option { - let path = match r#type { - ImportLauncherType::MultiMC => { - return find_multimc_path(); - } - ImportLauncherType::PrismLauncher => { - Some(dirs::data_dir()?.join("PrismLauncher")) - } - ImportLauncherType::ATLauncher => { - Some(dirs::data_dir()?.join("ATLauncher")) - } - ImportLauncherType::GDLauncher => { - Some(dirs::data_dir()?.join("gdlauncher_next")) - } - ImportLauncherType::Curseforge => { - let home = dirs::home_dir()?; - let primary = home.join("curseforge").join("minecraft"); - if primary.exists() { - return Some(primary); + let name = match r#type { + ImportLauncherType::MultiMC => "MultiMC", + ImportLauncherType::PrismLauncher => "Prism Launcher", + ImportLauncherType::ATLauncher => "ATLauncher", + ImportLauncherType::GDLauncher => "GDLauncher", + ImportLauncherType::Curseforge => "CurseForge", + ImportLauncherType::Unknown => return None, + }; + + installed_path(r#type, name) +} + +/// A launcher instances can be imported from, and where it keeps them. +#[derive(Debug, Clone, Serialize)] +pub struct LauncherSuggestion { + /// The importer that reads this launcher's instances. + pub launcher: String, + pub name: String, + /// Where it is, or where it would be on this system. + pub path: Option, + /// Whether it is actually there. + pub installed: bool, +} + +/// The launchers worth offering, with the importer each one is read by. +/// +/// PolyMC and other MultiMC forks keep their instances the way Prism does, so +/// that importer reads them as well. +const SUGGESTED_LAUNCHERS: &[(ImportLauncherType, &str)] = &[ + (ImportLauncherType::PrismLauncher, "Prism Launcher"), + (ImportLauncherType::MultiMC, "MultiMC"), + (ImportLauncherType::PrismLauncher, "PolyMC"), + (ImportLauncherType::ATLauncher, "ATLauncher"), + (ImportLauncherType::GDLauncher, "GDLauncher"), + (ImportLauncherType::Curseforge, "CurseForge"), +]; + +/// Every launcher that can be imported from, whether it is installed or not: +/// one that is not still gets the folder it would be in, to start looking from. +pub fn launcher_suggestions() -> Vec { + SUGGESTED_LAUNCHERS + .iter() + .map(|&(launcher, name)| { + let installed = installed_path(launcher, name); + LauncherSuggestion { + launcher: launcher.to_string(), + name: name.to_owned(), + path: installed + .clone() + .or_else(|| launcher_paths(name).into_iter().next()), + installed: installed.is_some(), } - Some(dirs::document_dir()?.join("curseforge").join("minecraft")) - } - ImportLauncherType::Unknown => None, + }) + .collect() +} + +/// Where a launcher of this name actually is, if it is installed. +fn installed_path(r#type: ImportLauncherType, name: &str) -> Option { + let paths = launcher_paths(name); + + // MultiMC is unpacked wherever the player put it, so its own config file is + // what tells one of those places from any other folder of that name. + if r#type == ImportLauncherType::MultiMC { + return paths + .into_iter() + .find(|path| path.join("multimc.cfg").exists()); + } + + paths.into_iter().find(|path| path.exists()) +} + +/// Everywhere a launcher of this name keeps its data on this system, likeliest +/// first. +fn launcher_paths(name: &str) -> Vec { + let data = dirs::data_dir(); + let home = dirs::home_dir(); + // Flatpak gives each app a data directory of its own. + let flatpak = |app: &str, folder: &str| { + home.as_ref().map(|home| { + home.join(".var") + .join("app") + .join(app) + .join("data") + .join(folder) + }) }; - let path = path?; - if path.exists() { Some(path) } else { None } + + match name { + "Prism Launcher" => [ + data.map(|data| data.join("PrismLauncher")), + flatpak("org.prismlauncher.PrismLauncher", "PrismLauncher"), + ] + .into_iter() + .flatten() + .collect(), + "PolyMC" => [ + data.map(|data| data.join("PolyMC")), + flatpak("org.polymc.PolyMC", "PolyMC"), + ] + .into_iter() + .flatten() + .collect(), + "MultiMC" => multimc_paths(), + "ATLauncher" => data + .map(|data| vec![data.join("ATLauncher")]) + .unwrap_or_default(), + "GDLauncher" => data + .map(|data| vec![data.join("gdlauncher_next")]) + .unwrap_or_default(), + "CurseForge" => [ + home.map(|home| home.join("curseforge").join("minecraft")), + dirs::document_dir() + .map(|docs| docs.join("curseforge").join("minecraft")), + ] + .into_iter() + .flatten() + .collect(), + _ => Vec::new(), + } } -/// Searches common locations for a MultiMC installation. +/// The common locations for a MultiMC installation. /// MultiMC stores data in its own application directory (not a standard data dir) -fn find_multimc_path() -> Option { +fn multimc_paths() -> Vec { let mut candidates: Vec = Vec::new(); // Linux/macOS: ~/.local/share/multimc is the typical location @@ -318,8 +412,6 @@ fn find_multimc_path() -> Option { } candidates - .into_iter() - .find(|p| p.join("multimc.cfg").exists()) } /// Checks if this PathBuf is a valid instance for the given launcher type diff --git a/packages/ui/src/assets/launchers/README.md b/packages/ui/src/assets/launchers/README.md new file mode 100644 index 0000000..4c7d7a6 --- /dev/null +++ b/packages/ui/src/assets/launchers/README.md @@ -0,0 +1,16 @@ +# Launcher logos + +The logos of the launchers instances can be imported from, shown beside their name in the import +dialog. + +| File | From | Licence | +| -------------------- | --------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | +| `prism-launcher.svg` | [PrismLauncher/PrismLauncher](https://github.com/PrismLauncher/PrismLauncher), `program_info` | [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/), unchanged | +| `polymc.svg` | [PolyMC/PolyMC](https://github.com/PolyMC/PolyMC), `program_info` | [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/), unchanged | +| `atlauncher.svg` | [ATLauncher/ATLauncher](https://github.com/ATLauncher/ATLauncher), `packaging/linux` | GPL-3.0, unchanged | +| `gdlauncher.png` | [gorilla-devs/GDLauncher](https://github.com/gorilla-devs/GDLauncher), `public/icon.png` | GPL-3.0, scaled down to 128×128 | +| `multimc.svg` | [MultiMC/Launcher](https://github.com/MultiMC/Launcher), `branding` | No licence given; shown to name the launcher, optimised with svgo | + +CurseForge's logo is the one Modrinth already ships in `@modrinth/assets`. + +A launcher without a logo here falls back to a badge with its initials. diff --git a/packages/ui/src/assets/launchers/atlauncher.svg b/packages/ui/src/assets/launchers/atlauncher.svg new file mode 100644 index 0000000..5dd0000 --- /dev/null +++ b/packages/ui/src/assets/launchers/atlauncher.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/launchers/gdlauncher.png b/packages/ui/src/assets/launchers/gdlauncher.png new file mode 100644 index 0000000000000000000000000000000000000000..9d369eb6d30a38821f97569a41fbb0f2346f7fc6 GIT binary patch literal 7218 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 LS3j3^P6 \ No newline at end of file diff --git a/packages/ui/src/assets/launchers/polymc.svg b/packages/ui/src/assets/launchers/polymc.svg new file mode 100644 index 0000000..c192d50 --- /dev/null +++ b/packages/ui/src/assets/launchers/polymc.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/ui/src/assets/launchers/prism-launcher.svg b/packages/ui/src/assets/launchers/prism-launcher.svg new file mode 100644 index 0000000..aeee843 --- /dev/null +++ b/packages/ui/src/assets/launchers/prism-launcher.svg @@ -0,0 +1,57 @@ + + + + Prism Launcher Logo + + + + + + + + + + + + + + + + + + + + + + + Prism Launcher Logo + 19/10/2022 + + + Prism Launcher + + + + + AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke + + + https://github.com/PrismLauncher/PrismLauncher + + + Prism Launcher + + + + + + + + + + + + + + + diff --git a/packages/ui/src/components/flows/creation-flow-modal/components/ImportInstanceStage.vue b/packages/ui/src/components/flows/creation-flow-modal/components/ImportInstanceStage.vue index a4dfcdb..5384d29 100644 --- a/packages/ui/src/components/flows/creation-flow-modal/components/ImportInstanceStage.vue +++ b/packages/ui/src/components/flows/creation-flow-modal/components/ImportInstanceStage.vue @@ -51,6 +51,7 @@ @update:model-value="toggleLauncherAll(launcher, $event)" @click.stop /> + {{ launcher.name }} @@ -75,6 +76,30 @@ + +
+ + {{ formatMessage(messages.otherLaunchers) }} + + +
+