@@ -256,125 +256,132 @@ private void InstallMods(object? sender, DoWorkEventArgs? e)
256256 for ( bool resolvedAllProvidedMods = false ; ! resolvedAllProvidedMods ; )
257257 {
258258 // Treat whole changeset as atomic
259- using ( TransactionScope transaction = CkanTransaction . CreateTransactionScope ( ) )
259+ using ( var transaction = CkanTransaction . CreateTransactionScope ( ) )
260260 {
261- try
261+ // Don't repeat these steps within the same transaction
262+ bool didUninstall = false ;
263+ bool didInstall = false ;
264+ // Re-use the transaction for providing mod prompts
265+ while ( ! resolvedAllProvidedMods
266+ && Transaction . Current ? . TransactionInformation . Status != TransactionStatus . Aborted )
262267 {
263- e . Result = new InstallResult ( false , changes ) ;
264- if ( ! canceled && toUninstall . Count > 0 )
265- {
266- installer . UninstallList ( toUninstall . Select ( m => m . identifier ) ,
267- ref possibleConfigOnlyDirs , registry_manager , false , toInstall ) ;
268- toUninstall . Clear ( ) ;
269- }
270- if ( ! canceled && toInstall . Count > 0 )
271- {
272- installer . InstallList ( toInstall , options , registry_manager , ref possibleConfigOnlyDirs ,
273- deduper , userAgent , downloader , autoInstalled , false ) ;
274- toInstall . Clear ( ) ;
275- }
276- if ( ! canceled && toUpgrade . Count > 0 )
277- {
278- installer . Upgrade ( toUpgrade , downloader , ref possibleConfigOnlyDirs , registry_manager ,
279- deduper , autoInstalled , skipFiles , true , false ) ;
280- toUpgrade . Clear ( ) ;
281- }
282- if ( canceled )
268+ try
283269 {
284270 e . Result = new InstallResult ( false , changes ) ;
285- throw new CancelledActionKraken ( ) ;
271+ if ( ! canceled && toUninstall . Count > 0 && ! didUninstall )
272+ {
273+ installer . UninstallList ( toUninstall . Select ( m => m . identifier ) ,
274+ ref possibleConfigOnlyDirs , registry_manager , false , toInstall ) ;
275+ didUninstall = true ;
276+ }
277+ if ( ! canceled && toInstall . Count > 0 && ! didInstall )
278+ {
279+ installer . InstallList ( toInstall , options , registry_manager , ref possibleConfigOnlyDirs ,
280+ deduper , userAgent , downloader , autoInstalled , false ) ;
281+ didInstall = true ;
282+ }
283+ if ( ! canceled && toUpgrade . Count > 0 )
284+ {
285+ installer . Upgrade ( toUpgrade , downloader , ref possibleConfigOnlyDirs , registry_manager ,
286+ deduper , autoInstalled , skipFiles , true , false ) ;
287+ }
288+ if ( canceled )
289+ {
290+ e . Result = new InstallResult ( false , changes ) ;
291+ throw new CancelledActionKraken ( ) ;
292+ }
293+ transaction . Complete ( ) ;
294+ resolvedAllProvidedMods = true ;
286295 }
287- transaction . Complete ( ) ;
288- resolvedAllProvidedMods = true ;
289- }
290- catch ( ModuleDownloadErrorsKraken k )
291- {
292- // Get full changeset (toInstall only includes user's selections, not dependencies)
293- var crit = CurrentInstance . VersionCriteria ( ) ;
294- var fullChangeset = new RelationshipResolver (
295- toInstall . Concat ( toUpgrade ) , toUninstall , options , registry , CurrentInstance . Game , crit
296- ) . ModList ( ) . ToList ( ) ;
297- DownloadsFailedDialog ? dfd = null ;
298- Util . Invoke ( this , ( ) =>
296+ catch ( ModuleDownloadErrorsKraken k )
299297 {
300- dfd = new DownloadsFailedDialog (
301- Properties . Resources . ModDownloadsFailedMessage ,
302- Properties . Resources . ModDownloadsFailedColHdr ,
303- Properties . Resources . ModDownloadsFailedAbortBtn ,
304- k . Exceptions . Select ( kvp => new KeyValuePair < object [ ] , Exception > (
305- fullChangeset . Where ( m => ( m . download ?? Enumerable . Empty < Uri > ( ) )
306- . IntersectsWith ( kvp . Key . download ?? Enumerable . Empty < Uri > ( ) ) )
307- . ToArray ( ) ,
308- kvp . Value ) ) ,
309- ( m1 , m2 ) => ( m1 as CkanModule ) ? . download == ( m2 as CkanModule ) ? . download ) ;
310- dfd . ShowDialog ( this ) ;
311- } ) ;
312- var skip = ( dfd ? . Wait ( ) ? . OfType < CkanModule > ( ) ?? Enumerable . Empty < CkanModule > ( ) )
313- . ToArray ( ) ;
314- var abort = dfd ? . Abort ?? false ;
315- dfd ? . Dispose ( ) ;
316- if ( abort )
317- {
318- canceled = true ;
319- e . Result = new InstallResult ( false , changes ) ;
320- throw new CancelledActionKraken ( ) ;
321- }
298+ // Get full changeset (toInstall only includes user's selections, not dependencies)
299+ var crit = CurrentInstance . VersionCriteria ( ) ;
300+ var fullChangeset = new RelationshipResolver (
301+ toInstall . Concat ( toUpgrade ) , toUninstall , options , registry , CurrentInstance . Game , crit
302+ ) . ModList ( ) . ToList ( ) ;
303+ DownloadsFailedDialog ? dfd = null ;
304+ Util . Invoke ( this , ( ) =>
305+ {
306+ dfd = new DownloadsFailedDialog (
307+ Properties . Resources . ModDownloadsFailedMessage ,
308+ Properties . Resources . ModDownloadsFailedColHdr ,
309+ Properties . Resources . ModDownloadsFailedAbortBtn ,
310+ k . Exceptions . Select ( kvp => new KeyValuePair < object [ ] , Exception > (
311+ fullChangeset . Where ( m => ( m . download ?? Enumerable . Empty < Uri > ( ) )
312+ . IntersectsWith ( kvp . Key . download ?? Enumerable . Empty < Uri > ( ) ) )
313+ . ToArray ( ) ,
314+ kvp . Value ) ) ,
315+ ( m1 , m2 ) => ( m1 as CkanModule ) ? . download == ( m2 as CkanModule ) ? . download ) ;
316+ dfd . ShowDialog ( this ) ;
317+ } ) ;
318+ var skip = ( dfd ? . Wait ( ) ? . OfType < CkanModule > ( ) ?? Enumerable . Empty < CkanModule > ( ) )
319+ . ToArray ( ) ;
320+ var abort = dfd ? . Abort ?? false ;
321+ dfd ? . Dispose ( ) ;
322+ if ( abort )
323+ {
324+ canceled = true ;
325+ e . Result = new InstallResult ( false , changes ) ;
326+ throw new CancelledActionKraken ( ) ;
327+ }
322328
323- if ( skip . Length > 0 )
324- {
325- // Remove mods from changeset that user chose to skip
326- // and any mods depending on them
327- var dependers = Registry . FindReverseDependencies (
328- skip . Select ( s => s . identifier ) . ToList ( ) ,
329- Array . Empty < CkanModule > ( ) ,
330- registry . InstalledModules . Select ( im => im . Module )
331- . Concat ( fullChangeset )
332- . ToArray ( ) ,
333- registry . InstalledDlls , registry . InstalledDlc ,
334- // Consider virtual dependencies satisfied so user can make a new choice if they skip
335- rel => rel . LatestAvailableWithProvides ( registry , stabilityTolerance , crit ) . Count > 1 )
336- . ToHashSet ( ) ;
337- toInstall . RemoveAll ( m => dependers . Contains ( m . identifier ) ) ;
338- }
329+ if ( skip . Length > 0 )
330+ {
331+ // Remove mods from changeset that user chose to skip
332+ // and any mods depending on them
333+ var dependers = Registry . FindReverseDependencies (
334+ skip . Select ( s => s . identifier ) . ToList ( ) ,
335+ Array . Empty < CkanModule > ( ) ,
336+ registry . InstalledModules . Select ( im => im . Module )
337+ . Concat ( fullChangeset )
338+ . ToArray ( ) ,
339+ registry . InstalledDlls , registry . InstalledDlc ,
340+ // Consider virtual dependencies satisfied so user can make a new choice if they skip
341+ rel => rel . LatestAvailableWithProvides ( registry , stabilityTolerance , crit ) . Count > 1 )
342+ . ToHashSet ( ) ;
343+ toInstall . RemoveAll ( m => dependers . Contains ( m . identifier ) ) ;
344+ }
339345
340- // Now we loop back around again
341- }
342- catch ( TooManyModsProvideKraken k )
343- {
344- // Prompt user to choose which mod to use
345- tabController . ShowTab ( ChooseProvidedModsTabPage . Name , 3 ) ;
346- Util . Invoke ( this , ( ) => StatusProgress . Visible = false ) ;
347- var repoData = ServiceLocator . Container . Resolve < RepositoryDataManager > ( ) ;
348- ChooseProvidedMods . LoadProviders (
349- k . Message ,
350- k . modules . OrderByDescending ( Manager . Cache . IsCached )
351- . ThenByDescending ( m => repoData . GetDownloadCount ( registry . Repositories . Values ,
352- m . identifier )
353- ?? 0 )
354- . ThenByDescending ( m => m . identifier == k . requested )
355- . ThenBy ( m => m . name )
356- . ToList ( ) ,
357- Manager . Cache ,
358- ServiceLocator . Container . Resolve < IConfiguration > ( ) ) ;
359- tabController . SetTabLock ( true ) ;
360- var chosen = ChooseProvidedMods . Wait ( ) ;
361- // Close the selection prompt
362- tabController . SetTabLock ( false ) ;
363- if ( chosen != null )
364- {
365- // User picked a mod, queue it up for installation
366- toInstall . Add ( chosen ) ;
367- autoInstalled . Add ( chosen ) ;
368- // DON'T return so we can loop around and try the above InstallList call again
369- tabController . ShowTab ( WaitTabPage . Name ) ;
370- tabController . HideTab ( ChooseProvidedModsTabPage . Name ) ;
371- Util . Invoke ( this , ( ) => StatusProgress . Visible = true ) ;
346+ // Now we loop back around again
372347 }
373- else
348+ catch ( TooManyModsProvideKraken k )
374349 {
375- tabController . HideTab ( ChooseProvidedModsTabPage . Name ) ;
376- e . Result = new InstallResult ( false , changes ) ;
377- throw new CancelledActionKraken ( ) ;
350+ // Prompt user to choose which mod to use
351+ tabController . ShowTab ( ChooseProvidedModsTabPage . Name , 3 ) ;
352+ Util . Invoke ( this , ( ) => StatusProgress . Visible = false ) ;
353+ var repoData = ServiceLocator . Container . Resolve < RepositoryDataManager > ( ) ;
354+ ChooseProvidedMods . LoadProviders (
355+ k . Message ,
356+ k . modules . OrderByDescending ( Manager . Cache . IsCached )
357+ . ThenByDescending ( m => repoData . GetDownloadCount ( registry . Repositories . Values ,
358+ m . identifier )
359+ ?? 0 )
360+ . ThenByDescending ( m => m . identifier == k . requested )
361+ . ThenBy ( m => m . name )
362+ . ToList ( ) ,
363+ Manager . Cache ,
364+ ServiceLocator . Container . Resolve < IConfiguration > ( ) ) ;
365+ tabController . SetTabLock ( true ) ;
366+ var chosen = ChooseProvidedMods . Wait ( ) ;
367+ // Close the selection prompt
368+ tabController . SetTabLock ( false ) ;
369+ if ( chosen != null )
370+ {
371+ // User picked a mod, queue it up for installation
372+ toInstall . Add ( chosen ) ;
373+ autoInstalled . Add ( chosen ) ;
374+ // DON'T return so we can loop around and try the above InstallList call again
375+ tabController . ShowTab ( WaitTabPage . Name ) ;
376+ tabController . HideTab ( ChooseProvidedModsTabPage . Name ) ;
377+ Util . Invoke ( this , ( ) => StatusProgress . Visible = true ) ;
378+ }
379+ else
380+ {
381+ tabController . HideTab ( ChooseProvidedModsTabPage . Name ) ;
382+ e . Result = new InstallResult ( false , changes ) ;
383+ throw new CancelledActionKraken ( ) ;
384+ }
378385 }
379386 }
380387 }
0 commit comments