/// <exception cref="IOException">Failed to apply the update.</exception> public async Task Update(Progress <double> progressCallback, CancellationToken cancellationToken) { var downloadTarget = await GetTempDownloadFilename(); // Need to store the filename because MoveTo changes it to the new filename var downloadFilename = downloadTarget.FullName; if (RemoteFile != null) { Console.WriteLine($"Attempting download of {TargetPath.Name} from source {RemoteFile.Source.Origin}"); await RetryHelper.RetryOnExceptionAsync(async() => await RemoteFile.Download(downloadTarget, progressCallback, cancellationToken), 2, TimeSpan.FromSeconds(10), cancellationToken); downloadTarget.Refresh(); if (!downloadTarget.Exists || downloadTarget.Length != RemoteFile.ItemSize) { throw new IOException($"Failed to download the update file {RemoteFile.Name} - the downloaded file doesn't exist or is corrupted"); } Console.WriteLine($"Downloaded {downloadTarget.Length} bytes successfully"); } retryDelete: try { Directory.CreateDirectory(Path.GetDirectoryName(TargetPath.FullName)); try { if (TargetPath.Exists) { Console.WriteLine($"Deleting old file {TargetPath.FullName}"); // Prevent issues removing readonly files TargetPath.Attributes = FileAttributes.Normal; TargetPath.Delete(); // Make sure the file gets deleted before continuing await Task.Delay(200, cancellationToken); } if (RemoteFile != null) { downloadTarget.MoveTo(TargetPath.FullName); } } catch (IOException) { if (RemoteFile != null) { await Task.Delay(1000, cancellationToken); downloadTarget.Replace(TargetPath.FullName, TargetPath.FullName + ".old", true); await Task.Delay(1000, cancellationToken); File.Delete(TargetPath.FullName + ".old"); } else { throw; } } } catch (IOException ex) { if (await ProcessWaiter.CheckForProcessesBlockingKoiDir() != true) { throw RetryHelper.DoNotAttemptToRetry(new IOException($"Failed to apply update {TargetPath.FullName} because of an IO issue - {ex.Message}", ex)); } goto retryDelete; } catch (SecurityException ex) { if (MessageBox.Show($"Failed to apply update {TargetPath.FullName} because of a security issue - {ex.Message}\n\nDo you want KK Manager to attempt to fix the issue? Click cancel if you want to abort.", "Could not apply update", MessageBoxButtons.OKCancel, MessageBoxIcon.Error) != DialogResult.OK) { throw; } var fixPermissions = ProcessTools.FixPermissions(InstallDirectoryHelper.GameDirectory.FullName); if (fixPermissions == null) { throw RetryHelper.DoNotAttemptToRetry(new IOException($"Failed to create file in directory {TargetPath.FullName} because of a security issue - {ex.Message}", ex)); } fixPermissions.WaitForExit(); goto retryDelete; } finally { try { File.Delete(downloadFilename); } catch (SystemException ex) { Console.WriteLine(ex); } } }
public virtual async Task <List <UpdateTask> > GetUpdateItems(CancellationToken cancellationToken) { var updateInfos = new List <UpdateInfo>(); var filenamesToTry = new[] { UpdateInfo.UpdateFileName, "Updates1.xml", "Updates2.xml" }; foreach (var fn in filenamesToTry) { Stream str = null; try { var downloadFileAsync = DownloadFileAsync(fn, cancellationToken); if (!await downloadFileAsync.WithTimeout(TimeSpan.FromSeconds(20), cancellationToken)) { throw new TimeoutException("Timeout when trying to download " + fn); } str = downloadFileAsync.Result; } catch (TimeoutException ex) { throw RetryHelper.DoNotAttemptToRetry(ex); } catch (FileNotFoundException) { } catch (OperationCanceledException) { throw; } catch (Exception ex) { Console.WriteLine($"Failed to download Updates file {fn} from {Origin} - {ex.Message}"); } if (str != null) { try { updateInfos.AddRange(UpdateInfo.ParseUpdateManifest(str, this)); } catch (OutdatedVersionException ex) { Console.WriteLine($"Failed to parse update manifest file {fn} from {Origin} - {ex.Message}"); throw; } catch (Exception ex) { Console.WriteLine($"Failed to parse update manifest file {fn} from {Origin} - {ex.ToStringDemystified()}"); } finally { str.Dispose(); } } } if (updateInfos.Count == 0) { throw new FileNotFoundException($"Failed to get update list from host {Origin} - check log for details."); } updateInfos.RemoveAll( info => { if (!info.CheckConditions()) { Console.WriteLine($"Skipping {info.GUID} because of conditions"); return(true); } return(false); }); var allResults = new List <UpdateTask>(); if (updateInfos.Any()) { foreach (var updateInfo in updateInfos) { _latestModifiedDate = DateTime.MinValue; var remoteItem = GetRemoteRootItem(updateInfo.ServerPath); if (remoteItem == null) { throw new DirectoryNotFoundException($"Could not find ServerPath: {updateInfo.ServerPath} in host: {Origin}"); } var versionEqualsComparer = GetVersionEqualsComparer(updateInfo); await Task.Run( () => { var results = ProcessDirectory( remoteItem, updateInfo.ClientPathInfo, updateInfo.Recursive, updateInfo.RemoveExtraClientFiles, versionEqualsComparer, cancellationToken); allResults.Add(new UpdateTask(updateInfo.Name ?? remoteItem.Name, results, updateInfo, _latestModifiedDate)); }, cancellationToken); } // If a task is expanded by other tasks, remove the items that other tasks expand from it foreach (var resultTask in allResults) { if (!string.IsNullOrEmpty(resultTask.Info.ExpandsGUID)) { Console.WriteLine($"Expanding task {resultTask.Info.ExpandsGUID} with task {resultTask.Info.GUID}"); ApplyExtendedItems(resultTask.Info.ExpandsGUID, resultTask.Items, allResults); } } } return(allResults); }