diff --git a/Giants.Launcher/Config.cs b/Giants.Launcher/Config.cs index 4185b2f..4d89d9d 100644 --- a/Giants.Launcher/Config.cs +++ b/Giants.Launcher/Config.cs @@ -11,6 +11,7 @@ { private const string defaultConfigFileName = "GiantsDefault.config"; private const string playerConfigFileName = "Giants.config"; + private bool dirty = false; private IDictionary defaultConfig = new Dictionary(); private IDictionary userConfig = new Dictionary(); @@ -35,6 +36,34 @@ } } + public void Write() + { + if (!this.dirty) + { + return; + } + + try + { + string userConfigFilePath = this.GetUserConfigPath(); + + Directory.CreateDirectory(Path.GetDirectoryName(userConfigFilePath)); + + using (var file = File.OpenWrite(userConfigFilePath)) + using (var streamWriter = new StreamWriter(file)) + { + string serializedConfig = JsonConvert.SerializeObject(this.userConfig, Formatting.Indented); + streamWriter.Write(serializedConfig); + } + + this.dirty = false; + } + catch (Exception e) + { + MessageBox.Show($"Unhandled exception saving updated configuration: {e}"); + } + } + public bool TryGetObject(string section, string key, object defaultValue, out object value) { value = defaultValue; @@ -87,6 +116,26 @@ return false; } + public void SetValue(string section, string key, dynamic value) + { + if (!this.userConfig.ContainsKey(section)) + { + this.userConfig.Add(section, new Dictionary()); + } + + dynamic sectionObject = this.userConfig[section]; + if (sectionObject != null && sectionObject.ContainsKey(key)) + { + sectionObject[key] = value; + } + else + { + sectionObject.Add(key, value); + } + + this.dirty = true; + } + private static IDictionary ReadConfig(string filePath) { string fileContents = File.ReadAllText(filePath); diff --git a/Giants.Launcher/Forms/LauncherForm.cs b/Giants.Launcher/Forms/LauncherForm.cs index b4fcbcd..d8c897e 100644 --- a/Giants.Launcher/Forms/LauncherForm.cs +++ b/Giants.Launcher/Forms/LauncherForm.cs @@ -41,8 +41,12 @@ namespace Giants.Launcher // Set window title this.SetTitle(); - // Read newer file-based game settings - this.config = new Config(); + this.updater = new Updater( + updateCompletedCallback: this.LauncherForm_DownloadCompletedCallback, + updateProgressCallback: this.LauncherForm_DownloadProgressCallback); + + // Read newer file-based game settings + this.config = new Config(); this.config.Read(); this.config.TryGetString(ConfigSections.Network, ConfigKeys.MasterServerHostName, ConfigDefaults.MasterServerHostNameDefault, out string baseUrl); @@ -72,12 +76,14 @@ namespace Giants.Launcher private void btnExit_Click(object sender, EventArgs e) { + this.config.Write(); Application.Exit(); } private void btnPlay_Click(object sender, EventArgs e) { GameSettings.Save(); + this.config.Write(); foreach (string c in Environment.GetCommandLineArgs()) { @@ -97,25 +103,44 @@ namespace Giants.Launcher gameProcess.Start(); Application.Exit(); } - catch(Exception ex) + catch (Exception ex) { MessageBox.Show(string.Format("Failed to launch game process at: {0}. {1}", this.gamePath, ex.Message), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } - private void btnOptions_Click(object sender, EventArgs e) + private async void btnOptions_Click(object sender, EventArgs e) { - OptionsForm form = new OptionsForm( + OptionsForm form = new OptionsForm( title: Resources.AppName + " Options", gamePath: this.gamePath, appName: ApplicationNames.Giants, - branchName: this.branchName, + currentBranchName: this.branchName, config: this.config, - branchesClient: this.branchHttpClient); + branchesClient: this.branchHttpClient) + { + StartPosition = FormStartPosition.CenterParent + }; - form.StartPosition = FormStartPosition.CenterParent; form.ShowDialog(); + + this.config.TryGetBool(ConfigSections.Update, ConfigKeys.EnableBranchSelection, defaultValue: false, out bool enableBranchSelection); + if (enableBranchSelection) + { + this.config.TryGetString(ConfigSections.Update, ConfigKeys.BranchName, defaultValue: ConfigDefaults.BranchNameDefault, out string branchName); + + if (!this.branchName.Equals(branchName)) + { + this.branchName = branchName; + + VersionInfo gameVersionInfo = await this.GetVersionInfo( + GetApplicationName(ApplicationType.Game), this.branchName); + + this.btnPlay.Enabled = false; + await this.updater.UpdateApplication(ApplicationType.Game, gameVersionInfo); + } + } } private async void LauncherForm_Load(object sender, EventArgs e) @@ -142,30 +167,15 @@ namespace Giants.Launcher GameSettings.Load(this.gamePath); if (GameSettings.Get("NoAutoUpdate") == 0) - { - Version gameVersion = VersionHelper.GetGameVersion(this.gamePath); - if (gameVersion == null) - { - string message = string.Format(Resources.AppNotFound, Resources.AppName); - MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); - Application.Exit(); - } + { + Task updateTask = this.CheckForUpdates(); + Task discordTask = this.UpdateDiscordStatus(); - var appVersions = new Dictionary() - { - [ApplicationType.Game] = gameVersion, - [ApplicationType.Launcher] = VersionHelper.GetLauncherVersion() - }; + await Task.WhenAll(updateTask, discordTask); + } + } - // Check for updates - Task updateTask = this.CheckForUpdates(appVersions); - Task discordTask = this.CheckDiscordStatus(); - - await Task.WhenAll(updateTask, discordTask); - } - } - - private async Task CheckDiscordStatus() + private async Task UpdateDiscordStatus() { try { @@ -181,21 +191,30 @@ namespace Giants.Launcher } } - private async Task CheckForUpdates(Dictionary appVersions) + private async Task CheckForUpdates() { - this.updater = new Updater( - appVersions: appVersions, - updateCompletedCallback: this.LauncherForm_DownloadCompletedCallback, - updateProgressCallback: this.LauncherForm_DownloadProgressCallback); + Task gameVersionInfo = this.GetVersionInfo( + GetApplicationName(ApplicationType.Game), this.branchName); - VersionInfo gameVersionInfo = await this.GetVersionInfo( - GetApplicationName(ApplicationType.Game)); + Task launcherVersionInfo = this.GetVersionInfo( + GetApplicationName(ApplicationType.Launcher), this.branchName); - if (this.updater.IsUpdateRequired(ApplicationType.Game, gameVersionInfo)) + Version localGameVersion = VersionHelper.GetGameVersion(this.gamePath); + Version localLauncherVersion = VersionHelper.GetLauncherVersion(); + + await Task.WhenAll(gameVersionInfo, launcherVersionInfo); + + if (this.updater.IsUpdateRequired(ApplicationType.Game, gameVersionInfo.Result, localGameVersion)) { this.btnPlay.Enabled = false; - await this.updater.UpdateApplication(ApplicationType.Game, gameVersionInfo); + await this.updater.UpdateApplication(ApplicationType.Game, gameVersionInfo.Result); } + + if (this.updater.IsUpdateRequired(ApplicationType.Launcher, launcherVersionInfo.Result, localLauncherVersion)) + { + this.btnPlay.Enabled = false; + await this.updater.UpdateApplication(ApplicationType.Launcher, launcherVersionInfo.Result); + } } private static string GetApplicationName(ApplicationType applicationType) @@ -204,17 +223,19 @@ namespace Giants.Launcher { case ApplicationType.Game: return ApplicationNames.Giants; + case ApplicationType.Launcher: + return ApplicationNames.GiantsLauncher; } - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(); } - private async Task GetVersionInfo(string appName) + private async Task GetVersionInfo(string appName, string branchName) { VersionInfo versionInfo; try { - versionInfo = await this.versionHttpClient.GetVersionInfoAsync(appName, this.branchName); + versionInfo = await this.versionHttpClient.GetVersionInfoAsync(appName, branchName); return versionInfo; } catch (ApiException ex) @@ -229,26 +250,6 @@ namespace Giants.Launcher } } - private async Task GetBranches(string appName) - { - VersionInfo versionInfo; - try - { - versionInfo = await this.versionHttpClient.GetVersionInfoAsync(appName, this.branchName); - return versionInfo; - } - catch (ApiException ex) - { - MessageBox.Show($"Exception retrieving branch information: {ex.StatusCode}"); - return null; - } - catch (Exception ex) - { - MessageBox.Show($"Exception retrieving branch information: {ex.Message}"); - return null; - } - } - private void LauncherForm_MouseDown(object sender, MouseEventArgs e) { // Force window to be draggable even though we have no menu bar @@ -314,6 +315,14 @@ namespace Giants.Launcher updaterProcess.Start(); + this.config.TryGetBool(ConfigSections.Update, ConfigKeys.EnableBranchSelection, defaultValue: false, out bool enableBranchSelection); + if (enableBranchSelection) + { + this.config.SetValue(ConfigSections.Update, ConfigKeys.BranchName, this.branchName); + } + + this.config.Write(); + Application.Exit(); return; } @@ -350,15 +359,6 @@ namespace Giants.Launcher private void SetTitle() { string title = Resources.AppName; - -#if DEBUG - title += " DEBUG"; -#endif - -#if BETA - title += " BETA"; -#endif - this.Text = title; } } diff --git a/Giants.Launcher/Forms/OptionsForm.Designer.cs b/Giants.Launcher/Forms/OptionsForm.Designer.cs index 207cf83..4b9cb00 100644 --- a/Giants.Launcher/Forms/OptionsForm.Designer.cs +++ b/Giants.Launcher/Forms/OptionsForm.Designer.cs @@ -239,6 +239,7 @@ // // cmbBranch // + this.cmbBranch.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbBranch.FormattingEnabled = true; this.cmbBranch.Location = new System.Drawing.Point(27, 17); this.cmbBranch.Name = "cmbBranch"; diff --git a/Giants.Launcher/Forms/OptionsForm.cs b/Giants.Launcher/Forms/OptionsForm.cs index 07cf63a..909cc76 100644 --- a/Giants.Launcher/Forms/OptionsForm.cs +++ b/Giants.Launcher/Forms/OptionsForm.cs @@ -13,7 +13,7 @@ namespace Giants.Launcher private readonly string gamePath = null; private readonly string appName; private readonly Config config; - private readonly string branchName; + private readonly string currentBranchName; private readonly bool enableBranchSelection; private readonly BranchesClient branchesClient; @@ -22,7 +22,7 @@ namespace Giants.Launcher string gamePath, string appName, Config config, - string branchName, + string currentBranchName, BranchesClient branchesClient) { this.InitializeComponent(); @@ -31,7 +31,7 @@ namespace Giants.Launcher this.gamePath = gamePath; this.appName = appName; this.config = config; - this.branchName = branchName; + this.currentBranchName = currentBranchName; this.branchesClient = branchesClient; this.config.TryGetBool(ConfigSections.Update, ConfigKeys.EnableBranchSelection, defaultValue: false, out bool enableBranchSelection); @@ -57,12 +57,21 @@ namespace Giants.Launcher { if (this.enableBranchSelection) { - var branches = await this.branchesClient.GetBranchesAsync(this.appName); + try + { + var branches = await this.branchesClient.GetBranchesAsync(this.appName); - cmbBranch.Items.AddRange(branches.ToArray()); + cmbBranch.Items.AddRange(branches.ToArray()); - BranchGroupBox.Visible = true; - cmbBranch.Visible = true; + cmbBranch.SelectedItem = this.currentBranchName; + + BranchGroupBox.Visible = true; + cmbBranch.Visible = true; + } + catch (Exception e) + { + MessageBox.Show($"Unhandled exception retrieving branch information from the server: {e.Message}"); + } } } @@ -110,14 +119,16 @@ namespace Giants.Launcher if (this.enableBranchSelection) { - this.cmbBranch.SelectedItem = this.branchName; + this.cmbBranch.SelectedItem = this.currentBranchName; } } private void PopulateAntialiasing() { - var antialiasingOptions = new List>(); - antialiasingOptions.Add(new KeyValuePair(Resources.OptionNone, 0)); + var antialiasingOptions = new List> + { + new KeyValuePair(Resources.OptionNone, 0) + }; var renderer = (RendererInfo)this.cmbRenderer.SelectedItem; if (renderer != null) @@ -268,6 +279,15 @@ namespace Giants.Launcher GameSettings.Save(); + if (this.enableBranchSelection) + { + string newBranch = this.cmbBranch.SelectedItem?.ToString(); + if (!string.IsNullOrEmpty(newBranch) && !newBranch.Equals(this.currentBranchName, StringComparison.OrdinalIgnoreCase)) + { + this.config.SetValue(ConfigSections.Update, ConfigKeys.BranchName, newBranch); + } + } + this.Close(); } diff --git a/Giants.Launcher/Updater/Updater.cs b/Giants.Launcher/Updater/Updater.cs index a170640..393bd7c 100644 --- a/Giants.Launcher/Updater/Updater.cs +++ b/Giants.Launcher/Updater/Updater.cs @@ -1,38 +1,35 @@ -using System; -using System.Collections.Generic; +using Giants.WebApi.Clients; +using System; using System.ComponentModel; using System.IO; using System.Net; using System.Threading.Tasks; using System.Windows.Forms; -using Giants.WebApi.Clients; namespace Giants.Launcher { public class Updater { - private readonly IDictionary appVersions; private readonly AsyncCompletedEventHandler updateCompletedCallback; private readonly DownloadProgressChangedEventHandler updateProgressCallback; public Updater( - IDictionary appVersions, AsyncCompletedEventHandler updateCompletedCallback, DownloadProgressChangedEventHandler updateProgressCallback) { - this.appVersions = appVersions; this.updateCompletedCallback = updateCompletedCallback; this.updateProgressCallback = updateProgressCallback; } - public bool IsUpdateRequired(ApplicationType applicationType, VersionInfo versionInfo) + public bool IsUpdateRequired(ApplicationType applicationType, VersionInfo remoteVersionInfo, Version localVersion) { - if (versionInfo != null && this.ToVersion(versionInfo.Version) > this.appVersions[applicationType]) + if (remoteVersionInfo?.InstallerUri != null + && this.ToVersion(remoteVersionInfo.Version) > localVersion) { // Display update prompt string updateMsg = applicationType == ApplicationType.Game ? - string.Format(Resources.UpdateAvailableText, this.ToVersion(versionInfo.Version).ToString()) : - string.Format(Resources.LauncherUpdateAvailableText, this.ToVersion(versionInfo.Version).ToString()); + string.Format(Resources.UpdateAvailableText, this.ToVersion(remoteVersionInfo.Version).ToString()) : + string.Format(Resources.LauncherUpdateAvailableText, this.ToVersion(remoteVersionInfo.Version).ToString()); if (MessageBox.Show(updateMsg, Resources.UpdateAvailableTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes) { diff --git a/Giants.Launcher/VersionHelper.cs b/Giants.Launcher/VersionHelper.cs index 5dd5654..58d1e31 100644 --- a/Giants.Launcher/VersionHelper.cs +++ b/Giants.Launcher/VersionHelper.cs @@ -15,6 +15,10 @@ } catch (Exception) { + string message = string.Format(Resources.AppNotFound, Resources.AppName); + MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + Application.Exit(); + return null; } } diff --git a/Giants.Services/Services/VersioningService.cs b/Giants.Services/Services/VersioningService.cs index 9f16cb5..880611d 100644 --- a/Giants.Services/Services/VersioningService.cs +++ b/Giants.Services/Services/VersioningService.cs @@ -39,7 +39,9 @@ namespace Giants.Services var versions = await this.versionCache.GetItems(); return versions - .Where(x => x.AppName.Equals(appName, StringComparison.Ordinal) && x.BranchName.Equals(branchName, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.AppName.Equals(appName, StringComparison.Ordinal) + && !string.IsNullOrEmpty(x.BranchName) + && x.BranchName.Equals(branchName, StringComparison.OrdinalIgnoreCase)) .FirstOrDefault(); } diff --git a/Giants.WebApi/Controllers/VersionController.cs b/Giants.WebApi/Controllers/VersionController.cs index d5edb64..2c30645 100644 --- a/Giants.WebApi/Controllers/VersionController.cs +++ b/Giants.WebApi/Controllers/VersionController.cs @@ -28,26 +28,9 @@ namespace Giants.WebApi.Controllers } [HttpGet] - public async Task GetVersionInfo(string appName) - { - ArgumentUtility.CheckStringForNullOrEmpty(appName); - - VersionInfo versionInfo = await this.versioningService.GetVersionInfo(appName, BranchConstants.DefaultBranchName); - - if (versionInfo == null) - { - throw new ArgumentException($"No version information for {appName} found.", appName); - } - - return this.mapper.Map(versionInfo); - } - - [HttpGet] - [MapToApiVersion("1.1")] public async Task GetVersionInfo(string appName, string branchName) { ArgumentUtility.CheckStringForNullOrEmpty(appName); - ArgumentUtility.CheckStringForNullOrEmpty(branchName); VersionInfo versionInfo = await this.versioningService.GetVersionInfo(appName, branchName);