// TODO: migrate to content-install.ts DI import dayjs from 'dayjs' import { get_project, get_version_many } from '@/helpers/cache.js' import { add_project_from_version, check_installed } from '@/helpers/profile.js' import { add_server_to_profile, get_profile_worlds, resolveManagedServerWorld, } from '@/helpers/worlds.ts' export const findPreferredVersion = (versions, project, instance) => { // When `project` is passed in from this stack trace: // - `installVersionDependencies` // - `install.js/install` - `installVersionDependencies` call // // ..then `project` is actually a `Dependency` struct of a cached `Version`. // `Dependency` does not have a `project_type` field, // so we default it to `mod`. // // If we don't default here, then this `.find` will ignore version/instance // loader mismatches, and you'll end up e.g. installing NeoForge mods for a // Fabric instance. const projectType = project.project_type ?? 'mod' // 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) && (projectType === '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 installVersionDependencies = async (profile, version, onDepInstalling) => { for (const dep of version.dependencies) { if (dep.dependency_type !== 'required') continue // disallow fabric api install on quilt if (dep.project_id === 'P7dR8mSH' && profile.loader === 'quilt') continue if (dep.version_id) { if (dep.project_id && (await check_installed(profile.path, dep.project_id))) continue if (dep.project_id && onDepInstalling) { const depProject = await get_project(dep.project_id, 'bypass').catch(() => null) if (depProject) onDepInstalling(depProject) } await add_project_from_version(profile.path, dep.version_id) } else { if (dep.project_id && (await check_installed(profile.path, dep.project_id))) continue const depProject = await get_project(dep.project_id, 'bypass') if (onDepInstalling) onDepInstalling(depProject) const depVersions = (await get_version_many(depProject.versions, 'bypass')).sort( (a, b) => dayjs(b.date_published) - dayjs(a.date_published), ) const latest = findPreferredVersion(depVersions, dep, profile) if (latest) { await add_project_from_version(profile.path, latest.id) } } } } export const getServerAddress = (javaServer) => { if (!javaServer) return null const { address } = javaServer return address } export const ensureManagedServerWorldExists = async (profilePath, serverName, serverAddress) => { if (!profilePath || !serverAddress) return try { const worlds = await get_profile_worlds(profilePath) const managedWorld = resolveManagedServerWorld(worlds, serverName, serverAddress) if (!managedWorld) { await add_server_to_profile(profilePath, serverName, serverAddress, 'prompt') } } catch (err) { console.error('Failed to ensure managed server world exists:', err) } }