From 91a3aafa67d8d621796e21bd3a7eb47377f95fa3 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Wed, 18 May 2011 22:50:38 +1200 Subject: [PATCH] Split the download/install logic into multiple files. --- OpenRA.Game/Download.cs | 61 ++++++ OpenRA.Game/OpenRA.Game.csproj | 3 +- OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj | 2 + .../Widgets/CncDownloadPackagesLogic.cs | 113 ++++++++++ .../Widgets/CncInstallFromCDLogic.cs | 112 ++++++++++ OpenRA.Mods.Cnc/Widgets/CncInstallLogic.cs | 206 ------------------ .../Widgets/Delegates/GameInitDelegate.cs | 27 --- 7 files changed, 290 insertions(+), 234 deletions(-) create mode 100644 OpenRA.Game/Download.cs create mode 100755 OpenRA.Mods.Cnc/Widgets/CncDownloadPackagesLogic.cs create mode 100755 OpenRA.Mods.Cnc/Widgets/CncInstallFromCDLogic.cs diff --git a/OpenRA.Game/Download.cs b/OpenRA.Game/Download.cs new file mode 100644 index 0000000000..134b0769ad --- /dev/null +++ b/OpenRA.Game/Download.cs @@ -0,0 +1,61 @@ +#region Copyright & License Information +/* + * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.ComponentModel; +using System.Net; + +namespace OpenRA +{ + public class Download + { + WebClient wc; + bool cancelled; + + public Download(string url, string path, Action onProgress, Action onComplete) + { + wc = new WebClient(); + wc.Proxy = null; + + wc.DownloadProgressChanged += (_,a) => onProgress(a); + wc.DownloadFileCompleted += (_,a) => onComplete(a, cancelled); + + Game.OnQuit += () => Cancel(); + wc.DownloadFileCompleted += (_,a) => {Game.OnQuit -= () => Cancel();}; + + wc.DownloadFileAsync(new Uri(url), path); + } + + public void Cancel() + { + Game.OnQuit -= () => Cancel(); + wc.CancelAsync(); + cancelled = true; + } + + public static string FormatErrorMessage(Exception e) + { + var ex = e as System.Net.WebException; + if (ex == null) return e.Message; + + switch(ex.Status) + { + case WebExceptionStatus.NameResolutionFailure: + case WebExceptionStatus.Timeout: + case WebExceptionStatus.ConnectFailure: + return "Cannot connect to remote server"; + case WebExceptionStatus.ProtocolError: + return "File not found on remote server"; + default: + return ex.Message; + } + } + } +} \ No newline at end of file diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index 38fc796f80..c81c2958c0 100755 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -1,4 +1,4 @@ - + Debug @@ -185,6 +185,7 @@ + diff --git a/OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj b/OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj index d8bb64f394..6a8ae680d5 100644 --- a/OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj +++ b/OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj @@ -85,6 +85,8 @@ + + diff --git a/OpenRA.Mods.Cnc/Widgets/CncDownloadPackagesLogic.cs b/OpenRA.Mods.Cnc/Widgets/CncDownloadPackagesLogic.cs new file mode 100755 index 0000000000..d2801c3eab --- /dev/null +++ b/OpenRA.Mods.Cnc/Widgets/CncDownloadPackagesLogic.cs @@ -0,0 +1,113 @@ +#region Copyright & License Information +/* + * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Net; +using OpenRA.FileFormats; +using OpenRA.Widgets; + +namespace OpenRA.Mods.Cnc.Widgets +{ + public class CncDownloadPackagesLogic : IWidgetDelegate + { + Widget panel; + Dictionary installData; + ProgressBarWidget progressBar; + LabelWidget statusLabel; + Action continueLoading; + + [ObjectCreator.UseCtor] + public CncDownloadPackagesLogic([ObjectCreator.Param] Widget widget, + [ObjectCreator.Param] Dictionary installData, + [ObjectCreator.Param] Action continueLoading) + { + this.installData = installData; + this.continueLoading = continueLoading; + + panel = widget.GetWidget("INSTALL_DOWNLOAD_PANEL"); + progressBar = panel.GetWidget("PROGRESS_BAR"); + statusLabel = panel.GetWidget("STATUS_LABEL"); + + ShowDownloadDialog(); + } + + void ShowDownloadDialog() + { + statusLabel.GetText = () => "Initializing..."; + progressBar.SetIndeterminate(true); + var retryButton = panel.GetWidget("RETRY_BUTTON"); + retryButton.IsVisible = () => false; + + var cancelButton = panel.GetWidget("CANCEL_BUTTON"); + + // Save the package to a temp file + var file = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + var dest = new string[] { Platform.SupportDir, "Content", "cnc" }.Aggregate(Path.Combine); + + Action onDownloadProgress = i => + { + if (progressBar.Indeterminate) + progressBar.SetIndeterminate(false); + + progressBar.Percentage = i.ProgressPercentage; + statusLabel.GetText = () => "Downloading {1}/{2} kB ({0}%)".F(i.ProgressPercentage, i.BytesReceived / 1024, i.TotalBytesToReceive / 1024); + }; + + Action onExtractProgress = s => + { + Game.RunAfterTick(() => statusLabel.GetText = () => s); + }; + + Action onError = s => + { + Game.RunAfterTick(() => + { + statusLabel.GetText = () => "Error: "+s; + retryButton.IsVisible = () => true; + }); + }; + + Action onDownloadComplete = (i, cancelled) => + { + if (i.Error != null) + { + onError(Download.FormatErrorMessage(i.Error)); + return; + } + else if (cancelled) + { + onError("Download cancelled"); + return; + } + + // Automatically extract + statusLabel.GetText = () => "Extracting..."; + progressBar.SetIndeterminate(true); + if (InstallUtils.ExtractZip(file, dest, onExtractProgress, onError)) + { + Game.RunAfterTick(() => + { + Widget.CloseWindow(); + continueLoading(); + }); + } + }; + + var dl = new Download(installData["PackageURL"], file, onDownloadProgress, onDownloadComplete); + + cancelButton.OnClick = () => { dl.Cancel(); Widget.CloseWindow(); }; + retryButton.OnClick = () => { dl.Cancel(); ShowDownloadDialog(); }; + } + } +} diff --git a/OpenRA.Mods.Cnc/Widgets/CncInstallFromCDLogic.cs b/OpenRA.Mods.Cnc/Widgets/CncInstallFromCDLogic.cs new file mode 100755 index 0000000000..ae14b0a76f --- /dev/null +++ b/OpenRA.Mods.Cnc/Widgets/CncInstallFromCDLogic.cs @@ -0,0 +1,112 @@ +#region Copyright & License Information +/* + * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.IO; +using System.Linq; +using System.Threading; +using OpenRA.FileFormats; +using OpenRA.Widgets; + +namespace OpenRA.Mods.Cnc.Widgets +{ + public class CncInstallFromCDLogic : IWidgetDelegate + { + Widget panel; + ProgressBarWidget progressBar; + LabelWidget statusLabel; + Action continueLoading; + + [ObjectCreator.UseCtor] + public CncInstallFromCDLogic([ObjectCreator.Param] Widget widget, + [ObjectCreator.Param] Action continueLoading) + { + this.continueLoading = continueLoading; + panel = widget.GetWidget("INSTALL_FROMCD_PANEL"); + progressBar = panel.GetWidget("PROGRESS_BAR"); + statusLabel = panel.GetWidget("STATUS_LABEL"); + + var backButton = panel.GetWidget("BACK_BUTTON"); + backButton.OnClick = Widget.CloseWindow; + backButton.IsVisible = () => false; + + var retryButton = panel.GetWidget("RETRY_BUTTON"); + retryButton.OnClick = PromptForCD; + retryButton.IsVisible = () => false; + + // TODO: Search obvious places (platform dependent) for CD + PromptForCD(); + } + + void PromptForCD() + { + progressBar.SetIndeterminate(true); + Game.Utilities.PromptFilepathAsync("Select CONQUER.MIX on the C&C CD", path => Game.RunAfterTick(() => Install(path))); + } + + void Install(string path) + { + var dest = new string[] { Platform.SupportDir, "Content", "cnc" }.Aggregate(Path.Combine); + var copyFiles = new string[] { "CONQUER.MIX", "DESERT.MIX", + "GENERAL.MIX", "SCORES.MIX", "SOUNDS.MIX", "TEMPERAT.MIX", "WINTER.MIX"}; + + var extractPackage = "INSTALL/SETUP.Z"; + var extractFiles = new string[] { "cclocal.mix", "speech.mix", "tempicnh.mix", "updatec.mix" }; + + progressBar.SetIndeterminate(false); + + var installCounter = 0; + var onProgress = (Action)(s => + { + progressBar.Percentage = installCounter*100/(copyFiles.Count() + extractFiles.Count()); + installCounter++; + + statusLabel.GetText = () => s; + }); + + var onError = (Action)(s => + { + Game.RunAfterTick(() => + { + statusLabel.GetText = () => "Error: "+s; + panel.GetWidget("RETRY_BUTTON").IsVisible = () => true; + panel.GetWidget("BACK_BUTTON").IsVisible = () => true; + }); + }); + + string source; + try + { + source = Path.GetDirectoryName(path); + } + catch (ArgumentException) + { + onError("Invalid path selected"); + return; + } + + var t = new Thread( _ => + { + if (!InstallUtils.CopyFiles(source, copyFiles, dest, onProgress, onError)) + return; + + if (!InstallUtils.ExtractFromPackage(source, extractPackage, extractFiles, dest, onProgress, onError)) + return; + + Game.RunAfterTick(() => + { + Widget.CloseWindow(); + continueLoading(); + }); + }) { IsBackground = true }; + t.Start(); + } + } +} diff --git a/OpenRA.Mods.Cnc/Widgets/CncInstallLogic.cs b/OpenRA.Mods.Cnc/Widgets/CncInstallLogic.cs index a07d675d1f..db64c390b4 100755 --- a/OpenRA.Mods.Cnc/Widgets/CncInstallLogic.cs +++ b/OpenRA.Mods.Cnc/Widgets/CncInstallLogic.cs @@ -10,13 +10,6 @@ using System; using System.Collections.Generic; -using System.ComponentModel; -using System.IO; -using System.Linq; -using System.Net; -using System.Threading; -using OpenRA.FileFormats; -using OpenRA.Mods.RA.Widgets.Delegates; using OpenRA.Widgets; namespace OpenRA.Mods.Cnc.Widgets @@ -43,7 +36,6 @@ namespace OpenRA.Mods.Cnc.Widgets panel.GetWidget("QUIT_BUTTON").OnClick = Game.Exit; - // TODO: panel.GetWidget("MODS_BUTTON").OnClick = () => { Widget.OpenWindow("MODS_PANEL", new WidgetArgs() @@ -55,202 +47,4 @@ namespace OpenRA.Mods.Cnc.Widgets }; } } - - public class CncInstallFromCDLogic : IWidgetDelegate - { - Widget panel; - ProgressBarWidget progressBar; - LabelWidget statusLabel; - Action continueLoading; - - [ObjectCreator.UseCtor] - public CncInstallFromCDLogic([ObjectCreator.Param] Widget widget, - [ObjectCreator.Param] Action continueLoading) - { - this.continueLoading = continueLoading; - panel = widget.GetWidget("INSTALL_FROMCD_PANEL"); - progressBar = panel.GetWidget("PROGRESS_BAR"); - statusLabel = panel.GetWidget("STATUS_LABEL"); - - var backButton = panel.GetWidget("BACK_BUTTON"); - backButton.OnClick = Widget.CloseWindow; - backButton.IsVisible = () => false; - - var retryButton = panel.GetWidget("RETRY_BUTTON"); - retryButton.OnClick = PromptForCD; - retryButton.IsVisible = () => false; - - // TODO: Search obvious places (platform dependent) for CD - PromptForCD(); - } - - void PromptForCD() - { - progressBar.SetIndeterminate(true); - Game.Utilities.PromptFilepathAsync("Select CONQUER.MIX on the C&C CD", path => Game.RunAfterTick(() => Install(path))); - } - - void Install(string path) - { - var dest = new string[] { Platform.SupportDir, "Content", "cnc" }.Aggregate(Path.Combine); - var copyFiles = new string[] { "CONQUER.MIX", "DESERT.MIX", - "GENERAL.MIX", "SCORES.MIX", "SOUNDS.MIX", "TEMPERAT.MIX", "WINTER.MIX"}; - - var extractPackage = "INSTALL/SETUP.Z"; - var extractFiles = new string[] { "cclocal.mix", "speech.mix", "tempicnh.mix", "updatec.mix" }; - - progressBar.SetIndeterminate(false); - - var installCounter = 0; - var onProgress = (Action)(s => - { - progressBar.Percentage = installCounter*100/(copyFiles.Count() + extractFiles.Count()); - installCounter++; - - statusLabel.GetText = () => s; - }); - - var onError = (Action)(s => - { - Game.RunAfterTick(() => - { - statusLabel.GetText = () => "Error: "+s; - panel.GetWidget("RETRY_BUTTON").IsVisible = () => true; - panel.GetWidget("BACK_BUTTON").IsVisible = () => true; - }); - }); - - string source; - try - { - source = Path.GetDirectoryName(path); - } - catch (ArgumentException) - { - onError("Invalid path selected"); - return; - } - - var t = new Thread( _ => - { - if (!InstallUtils.CopyFiles(source, copyFiles, dest, onProgress, onError)) - return; - - if (!InstallUtils.ExtractFromPackage(source, extractPackage, extractFiles, dest, onProgress, onError)) - return; - - Game.RunAfterTick(() => - { - Widget.CloseWindow(); - continueLoading(); - }); - }) { IsBackground = true }; - t.Start(); - } - } - - public class CncDownloadPackagesLogic : IWidgetDelegate - { - Widget panel; - Dictionary installData; - ProgressBarWidget progressBar; - LabelWidget statusLabel; - Action continueLoading; - - [ObjectCreator.UseCtor] - public CncDownloadPackagesLogic([ObjectCreator.Param] Widget widget, - [ObjectCreator.Param] Dictionary installData, - [ObjectCreator.Param] Action continueLoading) - { - this.installData = installData; - this.continueLoading = continueLoading; - - panel = widget.GetWidget("INSTALL_DOWNLOAD_PANEL"); - progressBar = panel.GetWidget("PROGRESS_BAR"); - statusLabel = panel.GetWidget("STATUS_LABEL"); - - ShowDownloadDialog(); - } - - - void ShowDownloadDialog() - { - statusLabel.GetText = () => "Initializing..."; - progressBar.SetIndeterminate(true); - var retryButton = panel.GetWidget("RETRY_BUTTON"); - retryButton.IsVisible = () => false; - - var cancelButton = panel.GetWidget("CANCEL_BUTTON"); - - // Save the package to a temp file - var file = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); - var dest = new string[] { Platform.SupportDir, "Content", "cnc" }.Aggregate(Path.Combine); - - Action onDownloadProgress = i => - { - if (progressBar.Indeterminate) - progressBar.SetIndeterminate(false); - - progressBar.Percentage = i.ProgressPercentage; - statusLabel.GetText = () => "Downloading {1}/{2} kB ({0}%)".F(i.ProgressPercentage, i.BytesReceived / 1024, i.TotalBytesToReceive / 1024); - }; - - Action onExtractProgress = s => - { - Game.RunAfterTick(() => statusLabel.GetText = () => s); - }; - - Action onError = s => - { - Game.RunAfterTick(() => - { - statusLabel.GetText = () => "Error: "+s; - retryButton.IsVisible = () => true; - }); - }; - - Action onDownloadComplete = (i, cancelled) => - { - if (i.Error != null) - { - var message = i.Error.Message; - var except = i.Error as System.Net.WebException; - if (except != null) - { - if (except.Status == WebExceptionStatus.ProtocolError) - message = "File not found on remote server"; - else if (except.Status == WebExceptionStatus.NameResolutionFailure || - except.Status == WebExceptionStatus.Timeout || - except.Status == WebExceptionStatus.ConnectFailure) - message = "Cannot connect to remote server"; - } - - onError(message); - return; - } - else if (cancelled) - { - onError("Download cancelled"); - return; - } - - // Automatically extract - statusLabel.GetText = () => "Extracting..."; - progressBar.SetIndeterminate(true); - if (InstallUtils.ExtractZip(file, dest, onExtractProgress, onError)) - { - Game.RunAfterTick(() => - { - Widget.CloseWindow(); - continueLoading(); - }); - } - }; - - var dl = new Download(installData["PackageURL"], file, onDownloadProgress, onDownloadComplete); - - cancelButton.OnClick = () => { dl.Cancel(); Widget.CloseWindow(); }; - retryButton.OnClick = () => { dl.Cancel(); ShowDownloadDialog(); }; - } - } } diff --git a/OpenRA.Mods.RA/Widgets/Delegates/GameInitDelegate.cs b/OpenRA.Mods.RA/Widgets/Delegates/GameInitDelegate.cs index 443382e9ad..183493e5b9 100755 --- a/OpenRA.Mods.RA/Widgets/Delegates/GameInitDelegate.cs +++ b/OpenRA.Mods.RA/Widgets/Delegates/GameInitDelegate.cs @@ -224,31 +224,4 @@ namespace OpenRA.Mods.RA.Widgets.Delegates new string[] { "cclocal.mix", "speech.mix", "tempicnh.mix", "updatec.mix" }, dest); } } - - public class Download - { - WebClient wc; - bool cancelled; - - public Download(string url, string path, Action onProgress, Action onComplete) - { - wc = new WebClient(); - wc.Proxy = null; - - wc.DownloadProgressChanged += (_,a) => onProgress(a); - wc.DownloadFileCompleted += (_,a) => onComplete(a, cancelled); - - Game.OnQuit += () => Cancel(); - wc.DownloadFileCompleted += (_,a) => {Game.OnQuit -= () => Cancel();}; - - wc.DownloadFileAsync(new Uri(url), path); - } - - public void Cancel() - { - Game.OnQuit -= () => Cancel(); - wc.CancelAsync(); - cancelled = true; - } - } }