@@ -60,8 +60,10 @@ import {
60
60
getJavaLatestManifest ,
61
61
getJavaManifest ,
62
62
getMcManifest ,
63
- getModrinthVersionManifest ,
64
63
getModrinthCategories ,
64
+ getModrinthProject ,
65
+ getModrinthVersionManifest ,
66
+ getModrinthVersions ,
65
67
getMultipleAddons ,
66
68
mcAuthenticate ,
67
69
mcInvalidate ,
@@ -73,7 +75,6 @@ import {
73
75
msExchangeCodeForAccessToken ,
74
76
msMinecraftProfile ,
75
77
msOAuthRefresh ,
76
- getModrinthVersions ,
77
78
getVersionsFromHashes
78
79
} from '../api' ;
79
80
import {
@@ -3600,9 +3601,15 @@ export function installMod(
3600
3601
/**
3601
3602
* @param {ModrinthVersion } version
3602
3603
* @param {string } instanceName
3604
+ * @param {string } gameVersion
3603
3605
* @param {Function } onProgress
3604
3606
*/
3605
- export function installModrinthMod ( version , instanceName , onProgress ) {
3607
+ export function installModrinthMod (
3608
+ version ,
3609
+ instanceName ,
3610
+ gameVersion ,
3611
+ onProgress
3612
+ ) {
3606
3613
return async ( dispatch , getState ) => {
3607
3614
const state = getState ( ) ;
3608
3615
const instancesPath = _getInstancesPath ( state ) ;
@@ -3617,45 +3624,48 @@ export function installModrinthMod(version, instanceName, onProgress) {
3617
3624
} )
3618
3625
) ;
3619
3626
3620
- const dependencies = ( await resolveModrinthDependencies ( version ) ) . filter (
3627
+ // TODO: this array sometimes contains an empty array?
3628
+ const dependencies = (
3629
+ await resolveModrinthDependencies ( version , gameVersion )
3630
+ ) . filter (
3621
3631
dep => existingMods . find ( mod => mod . fileID === dep . id ) === undefined
3622
3632
) ;
3623
3633
3624
3634
// install dependencies and the mod that we want
3625
3635
await pMap (
3626
3636
[ ...dependencies , version ] ,
3627
- async v => {
3628
- const primaryFile = v . files . find ( f => f . primary ) ;
3629
-
3630
- const destFile = path . join ( instancePath , 'mods' , primaryFile . filename ) ;
3631
- const tempFile = path . join ( _getTempPath ( state ) , primaryFile . filename ) ;
3632
-
3633
- // download the mod
3634
- await downloadFile ( tempFile , primaryFile . url , onProgress ) ;
3635
-
3636
- // add mod to the mods list in the instance's config file
3637
- await dispatch (
3638
- updateInstanceConfig ( instanceName , config => {
3639
- return {
3640
- ... config ,
3641
- mods : [
3642
- ...config . mods ,
3643
- ... [
3644
- {
3645
- source : MODRINTH ,
3646
- projectID : v . project_id ,
3647
- fileID : v . id ,
3648
- fileName : primaryFile . filename ,
3649
- displayName : primaryFile . filename ,
3650
- downloadUrl : primaryFile . url
3651
- }
3637
+ v => {
3638
+ v . files ?. forEach ( async file => {
3639
+ const destFile = path . join ( instancePath , 'mods' , file . filename ) ;
3640
+ const tempFile = path . join ( _getTempPath ( state ) , file . filename ) ;
3641
+
3642
+ // download the mod
3643
+ await downloadFile ( tempFile , file . url , onProgress ) ;
3644
+
3645
+ // add mod to the mods list in the instance's config file
3646
+ await dispatch (
3647
+ updateInstanceConfig ( instanceName , config => {
3648
+ return {
3649
+ ... config ,
3650
+ mods : [
3651
+ ... config . mods ,
3652
+ ...[
3653
+ {
3654
+ source : MODRINTH ,
3655
+ projectID : v . project_id ,
3656
+ fileID : v . id ,
3657
+ fileName : file . filename ,
3658
+ displayName : file . filename ,
3659
+ downloadUrl : file . url
3660
+ }
3661
+ ]
3652
3662
]
3653
- ]
3654
- } ;
3655
- } )
3656
- ) ;
3663
+ } ;
3664
+ } )
3665
+ ) ;
3657
3666
3658
- await fse . move ( tempFile , destFile , { overwrite : true } ) ;
3667
+ await fse . move ( tempFile , destFile , { overwrite : true } ) ;
3668
+ } ) ;
3659
3669
} ,
3660
3670
{ concurrency : 2 }
3661
3671
) ;
@@ -3664,24 +3674,46 @@ export function installModrinthMod(version, instanceName, onProgress) {
3664
3674
3665
3675
/**
3666
3676
* Recursively gets all the dependent versions of a given version and returns them in one array
3667
- * @param {ModrinthVersion } version
3677
+ * @param {ModrinthVersion } version The mod version to get the dependencies for
3678
+ * @param {string } gameVersion The required Minecraft version, so we can ensure dependencies are compatible
3668
3679
* @returns {Promise<ModrinthVersion[]> }
3669
3680
*/
3670
- async function resolveModrinthDependencies ( version ) {
3681
+ async function resolveModrinthDependencies ( version , gameVersion ) {
3671
3682
// TODO: Ideally this function should be aware of mods the user already has installed and ignore them
3672
3683
3684
+ // Note: version.dependencies[].version_id can sometimes be null.
3685
+ // In this case we use the given project_id and select the most recent compatible version.
3686
+
3673
3687
// Get the IDs for this version's required dependencies
3674
- const depVersionIDs = version . dependencies
3675
- . filter ( v => v . dependency_type === 'required' )
3676
- . map ( v => v . version_id ) ;
3688
+ const depVersionIDs = await pMap (
3689
+ version . dependencies . filter ( dep => dep . dependency_type === 'required' ) ,
3690
+ async dep => {
3691
+ if ( dep . version_id ) return dep . version_id ;
3692
+
3693
+ const project = await getModrinthProject ( dep . project_id ) ;
3694
+ const availableModVersions = await getModrinthVersions ( project . versions ) ;
3695
+
3696
+ // Get the latest compatible version
3697
+ const compatibleModVersions = availableModVersions
3698
+ . filter ( v => v . game_versions . includes ( gameVersion ) )
3699
+ . sort ( ( a , b ) => a . date_published - b . date_published ) ;
3700
+ // prioritise stable releases, fall back to unstable releases if no compatible stable releases exist
3701
+ const latestCompatibleModVersion =
3702
+ compatibleModVersions . find ( v => v . version_type === 'release' ) ??
3703
+ compatibleModVersions . find ( v => v . version_type === 'beta' ) ??
3704
+ compatibleModVersions . find ( v => v . version_type === 'alpha' ) ;
3705
+
3706
+ return latestCompatibleModVersion . id ;
3707
+ }
3708
+ ) ;
3677
3709
3678
3710
// If this version does not depend on anything, return nothing
3679
3711
if ( depVersionIDs . length === 0 ) return [ ] ;
3680
3712
3681
3713
// If we do have dependencies, get the version objects for each of those and recurse on those
3682
3714
const depVersions = await getModrinthVersions ( depVersionIDs ) ;
3683
3715
const subDepVersions = await pMap ( depVersions , async v =>
3684
- resolveModrinthDependencies ( v )
3716
+ resolveModrinthDependencies ( v , gameVersion )
3685
3717
) ;
3686
3718
3687
3719
return [ ...depVersions , ...subDepVersions ] ;
@@ -3702,6 +3734,7 @@ export const deleteMod = (instanceName, mod) => {
3702
3734
} ;
3703
3735
} ;
3704
3736
3737
+ // TODO: Support Modrinth here
3705
3738
export const updateMod = (
3706
3739
instanceName ,
3707
3740
mod ,
0 commit comments