From a2c07c92f802ccab169443bc3d4bb57c5234769e Mon Sep 17 00:00:00 2001 From: Juhan Oskar Hennoste Date: Wed, 27 Aug 2025 16:47:39 +0300 Subject: [PATCH] Fix and unify version selection when installing mods and filtering (#4252) * Fix and unify version selection when installing mods * Update version list filters to match install version selection logic * Fix lint issues --------- Co-authored-by: Cal H. --- .../ui/install_flow/ModInstallModal.vue | 28 ++++------ apps/app-frontend/src/pages/project/Index.vue | 22 +++++++- apps/app-frontend/src/store/install.js | 54 ++++++++++++------- 3 files changed, 66 insertions(+), 38 deletions(-) diff --git a/apps/app-frontend/src/components/ui/install_flow/ModInstallModal.vue b/apps/app-frontend/src/components/ui/install_flow/ModInstallModal.vue index ccf6586ed..db93af568 100644 --- a/apps/app-frontend/src/components/ui/install_flow/ModInstallModal.vue +++ b/apps/app-frontend/src/components/ui/install_flow/ModInstallModal.vue @@ -22,7 +22,11 @@ import { get, list, } from '@/helpers/profile' -import { installVersionDependencies } from '@/store/install.js' +import { + findPreferredVersion, + installVersionDependencies, + isVersionCompatible, +} from '@/store/install.js' const { handleError } = injectNotificationManager() const router = useRouter() @@ -49,14 +53,11 @@ const shownProfiles = computed(() => return profile.name.toLowerCase().includes(searchFilter.value.toLowerCase()) }) .filter((profile) => { - const loaders = versions.value.flatMap((v) => v.loaders) - - return ( - versions.value.flatMap((v) => v.game_versions).includes(profile.game_version) && - (project.value.project_type === 'mod' - ? loaders.includes(profile.loader) || loaders.includes('minecraft') - : true) - ) + const version = { + game_versions: versions.value.flatMap((v) => v.game_versions), + loaders: versions.value.flatMap((v) => v.loaders), + } + return isVersionCompatible(version, project.value, profile) }), ) @@ -94,14 +95,7 @@ defineExpose({ async function install(instance) { instance.installing = true - const version = versions.value.find((v) => { - return ( - v.game_versions.includes(instance.game_version) && - (project.value.project_type === 'mod' - ? v.loaders.includes(instance.loader) || v.loaders.includes('minecraft') - : true) - ) - }) + const version = findPreferredVersion(versions.value, project.value, instance) if (!version) { instance.installing = false diff --git a/apps/app-frontend/src/pages/project/Index.vue b/apps/app-frontend/src/pages/project/Index.vue index fee1f2526..0844ca550 100644 --- a/apps/app-frontend/src/pages/project/Index.vue +++ b/apps/app-frontend/src/pages/project/Index.vue @@ -96,7 +96,7 @@ label: 'Versions', href: { path: `/project/${$route.params.id}/versions`, - query: { l: instance?.loader, g: instance?.game_version }, + query: instanceFilters, }, subpages: ['version'], }, @@ -154,7 +154,7 @@ import { import { openUrl } from '@tauri-apps/plugin-opener' import dayjs from 'dayjs' import relativeTime from 'dayjs/plugin/relativeTime' -import { ref, shallowRef, watch } from 'vue' +import { computed, ref, shallowRef, watch } from 'vue' import { useRoute, useRouter } from 'vue-router' import ContextMenu from '@/components/ui/ContextMenu.vue' @@ -186,6 +186,24 @@ const instanceProjects = ref(null) const installed = ref(false) const installedVersion = ref(null) +const instanceFilters = computed(() => { + if (!instance.value) { + return {} + } + + const loaders = [] + if (data.value.project_type === 'mod') { + if (instance.value.loader !== 'vanilla') { + loaders.push(instance.value.loader) + } + if (instance.value.loader === 'vanilla' || data.value.loaders.includes('datapack')) { + loaders.push('datapack') + } + } + + return { l: loaders, g: instance.value.game_version } +}) + const [allLoaders, allGameVersions] = await Promise.all([ get_loaders().catch(handleError).then(ref), get_game_versions().catch(handleError).then(ref), diff --git a/apps/app-frontend/src/store/install.js b/apps/app-frontend/src/store/install.js index 1e9f9486a..14988763a 100644 --- a/apps/app-frontend/src/store/install.js +++ b/apps/app-frontend/src/store/install.js @@ -41,6 +41,31 @@ export const useInstall = defineStore('installStore', { }, }) +export const findPreferredVersion = (versions, project, instance) => { + // If we can find a version using strictly the instance loader then prefer that + let version = versions.find( + (v) => + v.game_versions.includes(instance.game_version) && + (project.project_type === 'mod' ? v.loaders.includes(instance.loader) : true), + ) + + if (!version) { + // Otherwise use first compatible version (in addition to versions with the instance loader this includes datapacks) + version = versions.find((v) => isVersionCompatible(v, project, instance)) + } + + return version +} + +export const isVersionCompatible = (version, project, instance) => { + return ( + version.game_versions.includes(instance.game_version) && + (project.project_type === 'mod' + ? version.loaders.includes(instance.loader) || version.loaders.includes('datapack') + : true) + ) +} + export const install = async ( projectId, versionId, @@ -90,27 +115,16 @@ export const install = async ( let version if (versionId) { - version = projectVersions.find((x) => x.id === versionId) + version = projectVersions.find((v) => v.id === versionId) } else { - version = projectVersions.find( - (v) => - v.game_versions.includes(instance.game_version) && - (project.project_type === 'mod' - ? v.loaders.includes(instance.loader) || v.loaders.includes('minecraft') - : true), - ) + version = findPreferredVersion(projectVersions, project, instance) } if (!version) { version = projectVersions[0] } - if ( - version.game_versions.includes(instance.game_version) && - (project.project_type === 'mod' - ? version.loaders.includes(instance.loader) || version.loaders.includes('minecraft') - : true) - ) { + if (isVersionCompatible(version, project, instance, true)) { for (const [path, file] of Object.entries(instanceProjects)) { if (file.metadata && file.metadata.project_id === project.id) { await remove_project(instance.path, path) @@ -142,10 +156,14 @@ export const install = async ( ) } } else { - const versions = (await get_version_many(project.versions)).sort( + let versions = (await get_version_many(project.versions)).sort( (a, b) => dayjs(b.date_published) - dayjs(a.date_published), ) + if (versionId) { + versions = versions.filter((v) => v.id === versionId) + } + const install = useInstall() install.showModInstallModal(project, versions, callback) } @@ -162,7 +180,7 @@ export const install = async ( // - If no version is selected, we look check the instance for versions to select based on the versions // - If there are no versions, we show the incompat modal // - If a version is selected, and the version is incompatible, we show the incompat modal - // - Version is inarlled, as well as version dependencies + // - Version is installed, as well as version dependencies } export const installVersionDependencies = async (profile, version) => { @@ -182,9 +200,7 @@ export const installVersionDependencies = async (profile, version) => { (a, b) => dayjs(b.date_published) - dayjs(a.date_published), ) - const latest = depVersions.find( - (v) => v.game_versions.includes(profile.game_version) && v.loaders.includes(profile.loader), - ) + const latest = findPreferredVersion(depVersions, dep, profile) if (latest) { await add_project_from_version(profile.path, latest.id) }