private async Task InstallPackages(List <RemotePackage> toInstall, string tribesExePath) { try { BroadcastUpdatePhase(UpdatePhase.Preparing); if (!tribesExePath.ToLower().EndsWith(".exe")) { throw new Exception($"Invalid tribes path {tribesExePath}"); } // Exe is <basepath>/Binaries/Win32/TribesAscend.exe // So the directory two levels up is the base path of the install string tribesBasePath = Path.Combine(Path.GetDirectoryName(tribesExePath), "..", ".."); // Clear temp directory if it exists if (Directory.Exists("./tmp")) { Directory.Delete($"./tmp", true); } Directory.CreateDirectory("./tmp"); double progressBarValue = 0; BroadcastUpdatePhase(UpdatePhase.Downloading); // Download compressed packages to temp folder List <string> archives = await DownloadPackages(toInstall, ".", (percentage) => { // Half the progress bar for download, then half for extract / copy double pct = progressBarValue + 0.5 * percentage; OnProgressTick?.Invoke(this, new OnProgressTickEventArgs(pct)); }); progressBarValue = 0.5; BroadcastUpdatePhase(UpdatePhase.Extracting); // Extract packages and delete the zips await ExtractArchives(archives, (idx, totalZips) => { // 25% of progress bar for extracts double pct = progressBarValue + 0.25 * ((double)idx + 1) / totalZips; OnProgressTick?.Invoke(this, new OnProgressTickEventArgs(pct)); }); progressBarValue = 0.75; BroadcastUpdatePhase(UpdatePhase.Copying); // For each package we downloaded, copy its files into the local dir / config dir / Tribes dir await CopyDownloadedPackages("./tmp", ".", tribesBasePath, (idx, totalPackages) => { // 25% of progress bar for package copy double pct = progressBarValue + 0.25 * ((double)idx + 1) / totalPackages; OnProgressTick?.Invoke(this, new OnProgressTickEventArgs(pct)); }); BroadcastUpdatePhase(UpdatePhase.Finalising); // Save the new installed package manifest UpdateInstalledPackageState(toInstall); // Delete temp directory Directory.Delete($"./tmp", true); BroadcastUpdatePhase(UpdatePhase.NotUpdating); // Raise finished event OnUpdateComplete?.Invoke(this, new EventArgs()); } catch (Exception e) { // Reset update phase BroadcastUpdatePhase(UpdatePhase.NotUpdating); throw e; } }
private async Task PerformUpdateInternal() { Dictionary <string, double> filesToDownload = GetFilesNeedingUpdate(); // Download to temp folder using (var httpClient = new HttpClient()) { // Download files in batches of 10, if there are at least 10 to download var batchSize = filesToDownload.Count > 10 ? 10 : 1; var fileBatches = filesToDownload.Keys .Select((f, i) => { int dirSplitPos = Math.Max(0, Math.Max(f.LastIndexOf('/'), f.LastIndexOf('\\'))); string relativeDir = f.Substring(0, dirSplitPos); string dir = $"{LocalBasePath}/tmp/{relativeDir}"; return(new { Idx = i, Filename = f, Directory = dir }); }) .Batch(batchSize) .Select((b, i) => new { Idx = i, Batch = b }); var numBatches = fileBatches.Count(); foreach (var batch in fileBatches) { // Create directories for this batch if required foreach (var file in batch.Batch) { if (!Directory.Exists(file.Directory)) { Directory.CreateDirectory(file.Directory); } } var tasks = batch.Batch.Select(async(file) => { var response = await httpClient.GetAsync(new Uri($"{RemoteBaseUrl}/{file.Filename}")); using (var memStream = response.Content.ReadAsStreamAsync().Result) { using (var fileStream = File.Create($"{LocalBasePath}/tmp/{file.Filename}")) { memStream.CopyTo(fileStream); } } }); await Task.WhenAll(tasks); double pct = ((double)batch.Idx + 1) / numBatches; OnProgressTick?.Invoke(this, new OnProgressTickEventArgs(pct)); } } // Copy files out foreach (string filename in filesToDownload.Keys) { string copyLocation; if (filename.StartsWith("!CONFIG/")) { copyLocation = $"{ConfigBasePath}/{filename.Replace("!CONFIG/", "")}"; } else { copyLocation = $"{LocalBasePath}/{filename}"; } // Create directory if required string dir = new FileInfo(copyLocation).Directory.FullName; if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } // Copy the file File.Copy($"{LocalBasePath}/tmp/{filename}", copyLocation, true); } // Delete temp directory Directory.Delete($"{LocalBasePath}/tmp", true); // Download the current version manifest // TODO: THIS var newLocalManifest = XElement.Load($"{RemoteBaseUrl}/version.xml"); newLocalManifest.Save($"{LocalBasePath}/version.xml"); // Raise finished event OnUpdateComplete?.Invoke(this, new EventArgs()); }