Example #1
0
        /// <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);
        }