diff --git a/Core/GameInstance.cs b/Core/GameInstance.cs index 2cacb0a95d..1a520f303a 100644 --- a/Core/GameInstance.cs +++ b/Core/GameInstance.cs @@ -132,6 +132,10 @@ private void SetupCkanDirectories(bool scan = true) log.InfoFormat("Initialised {0}", CkanDir()); } + #endregion + + #region Settings + public void SetCompatibleVersions(List compatibleVersions) { this._compatibleVersions = compatibleVersions.Distinct().ToList(); @@ -178,6 +182,19 @@ public List GetCompatibleVersions() return new List(this._compatibleVersions); } + public HashSet GetSuppressedCompatWarningIdentifiers => + SuppressedCompatWarningIdentifiers.LoadFrom(Version(), SuppressedCompatWarningIdentifiersFile).Identifiers; + + public void AddSuppressedCompatWarningIdentifiers(HashSet idents) + { + var scwi = SuppressedCompatWarningIdentifiers.LoadFrom(Version(), SuppressedCompatWarningIdentifiersFile); + scwi.Identifiers.UnionWith(idents); + scwi.SaveTo(SuppressedCompatWarningIdentifiersFile); + } + + private string SuppressedCompatWarningIdentifiersFile => + Path.Combine(CkanDir(), "suppressed_compat_warning_identifiers.json"); + #endregion #region KSP Directory Detection and Versioning diff --git a/Core/SuppressedCompatWarningIdentifiers.cs b/Core/SuppressedCompatWarningIdentifiers.cs new file mode 100644 index 0000000000..525f0ea968 --- /dev/null +++ b/Core/SuppressedCompatWarningIdentifiers.cs @@ -0,0 +1,44 @@ +using System; +using System.IO; +using System.Collections.Generic; +using Newtonsoft.Json; +using log4net; + +using CKAN.Versioning; + +namespace CKAN +{ + class SuppressedCompatWarningIdentifiers + { + public GameVersion GameVersionWhenWritten; + public HashSet Identifiers = new HashSet(); + + public static SuppressedCompatWarningIdentifiers LoadFrom(GameVersion gameVer, string filename) + { + try + { + var saved = JsonConvert.DeserializeObject(File.ReadAllText(filename)); + // Reset warnings if e.g. Steam auto-updates the game + if (saved.GameVersionWhenWritten == gameVer) + { + return saved; + } + } + catch (Exception exc) + { + log.Debug("Failed to load", exc); + } + return new SuppressedCompatWarningIdentifiers() + { + GameVersionWhenWritten = gameVer + }; + } + + public void SaveTo(string filename) + { + File.WriteAllText(filename, JsonConvert.SerializeObject(this)); + } + + private static readonly ILog log = LogManager.GetLogger(typeof(SuppressedCompatWarningIdentifiers)); + } +} diff --git a/GUI/Dialogs/YesNoDialog.Designer.cs b/GUI/Dialogs/YesNoDialog.Designer.cs index 936403e24b..baa1e252ca 100644 --- a/GUI/Dialogs/YesNoDialog.Designer.cs +++ b/GUI/Dialogs/YesNoDialog.Designer.cs @@ -32,6 +32,7 @@ private void InitializeComponent() System.ComponentModel.ComponentResourceManager resources = new SingleAssemblyComponentResourceManager(typeof(YesNoDialog)); this.panel1 = new System.Windows.Forms.Panel(); this.DescriptionLabel = new TransparentTextBox(); + this.SuppressCheckbox = new System.Windows.Forms.CheckBox(); this.YesButton = new System.Windows.Forms.Button(); this.NoButton = new System.Windows.Forms.Button(); this.panel1.SuspendLayout(); @@ -63,6 +64,18 @@ private void InitializeComponent() this.DescriptionLabel.BorderStyle = System.Windows.Forms.BorderStyle.None; resources.ApplyResources(this.DescriptionLabel, "DescriptionLabel"); // + // SuppressCheckbox + // + this.SuppressCheckbox.AutoSize = false; + this.SuppressCheckbox.Anchor = ((System.Windows.Forms.AnchorStyles)(System.Windows.Forms.AnchorStyles.Bottom + | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)); + this.SuppressCheckbox.Location = new System.Drawing.Point(13, 80); + this.SuppressCheckbox.Name = "SuppressCheckbox"; + this.SuppressCheckbox.Size = new System.Drawing.Size(225, 50); + this.SuppressCheckbox.TabIndex = 1; + this.SuppressCheckbox.UseVisualStyleBackColor = true; + resources.ApplyResources(this.SuppressCheckbox, "SuppressCheckbox"); + // // YesButton // this.YesButton.Anchor = ((System.Windows.Forms.AnchorStyles)(System.Windows.Forms.AnchorStyles.Bottom @@ -71,7 +84,7 @@ private void InitializeComponent() this.YesButton.Location = new System.Drawing.Point(250, 92); this.YesButton.Name = "YesButton"; this.YesButton.Size = new System.Drawing.Size(75, 23); - this.YesButton.TabIndex = 1; + this.YesButton.TabIndex = 2; this.YesButton.UseVisualStyleBackColor = true; resources.ApplyResources(this.YesButton, "YesButton"); // @@ -83,7 +96,7 @@ private void InitializeComponent() this.NoButton.Location = new System.Drawing.Point(331, 92); this.NoButton.Name = "NoButton"; this.NoButton.Size = new System.Drawing.Size(75, 23); - this.NoButton.TabIndex = 2; + this.NoButton.TabIndex = 3; this.NoButton.UseVisualStyleBackColor = true; resources.ApplyResources(this.NoButton, "NoButton"); // @@ -92,6 +105,7 @@ private void InitializeComponent() this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(418, 127); + this.Controls.Add(this.SuppressCheckbox); this.Controls.Add(this.NoButton); this.Controls.Add(this.YesButton); this.Controls.Add(this.panel1); @@ -109,6 +123,7 @@ private void InitializeComponent() private System.Windows.Forms.Panel panel1; private TransparentTextBox DescriptionLabel; + private System.Windows.Forms.CheckBox SuppressCheckbox; private System.Windows.Forms.Button YesButton; private System.Windows.Forms.Button NoButton; } diff --git a/GUI/Dialogs/YesNoDialog.cs b/GUI/Dialogs/YesNoDialog.cs index 7ebe8793d9..d50a6a41b1 100644 --- a/GUI/Dialogs/YesNoDialog.cs +++ b/GUI/Dialogs/YesNoDialog.cs @@ -16,30 +16,57 @@ public YesNoDialog() public DialogResult ShowYesNoDialog(Form parentForm, string text, string yesText = null, string noText = null) { - task = new TaskCompletionSource(); + task = new TaskCompletionSource>(); Util.Invoke(parentForm, () => { - var height = StringHeight(text, ClientSize.Width - 25) + 2 * 54; - DescriptionLabel.Text = text; - DescriptionLabel.TextAlign = text.Contains("\n") - ? HorizontalAlignment.Left - : HorizontalAlignment.Center; - DescriptionLabel.ScrollBars = height < maxHeight - ? ScrollBars.None - : ScrollBars.Vertical; - YesButton.Text = yesText ?? defaultYes; - NoButton.Text = noText ?? defaultNo; - ClientSize = new Size( - ClientSize.Width, - Math.Min(maxHeight, height) - ); - task.SetResult(ShowDialog(parentForm)); + Setup(text, yesText, noText); + task.SetResult(new Tuple(ShowDialog(parentForm), SuppressCheckbox.Checked)); + }); + + return task.Task.Result.Item1; + } + + public Tuple ShowSuppressableYesNoDialog(Form parentForm, string text, string suppressText, string yesText = null, string noText = null) + { + task = new TaskCompletionSource>(); + + Util.Invoke(parentForm, () => + { + SetupSuppressable(text, yesText, noText, suppressText); + task.SetResult(new Tuple(ShowDialog(parentForm), SuppressCheckbox.Checked)); }); return task.Task.Result; } + private void Setup(string text, string yesText, string noText) + { + var height = StringHeight(text, ClientSize.Width - 25) + 2 * 54; + DescriptionLabel.Text = text; + DescriptionLabel.TextAlign = text.Contains("\n") + ? HorizontalAlignment.Left + : HorizontalAlignment.Center; + DescriptionLabel.ScrollBars = height < maxHeight + ? ScrollBars.None + : ScrollBars.Vertical; + YesButton.Text = yesText ?? defaultYes; + NoButton.Text = noText ?? defaultNo; + SuppressCheckbox.Visible = false; + ClientSize = new Size( + ClientSize.Width, + Math.Min(maxHeight, height) + ); + } + + private void SetupSuppressable(string text, string yesText, string noText, string suppressText) + { + Setup(text, yesText, noText); + SuppressCheckbox.Checked = false; + SuppressCheckbox.Text = suppressText; + SuppressCheckbox.Visible = true; + } + /// /// Simple syntactic sugar around Graphics.MeasureString /// @@ -59,7 +86,7 @@ public void HideYesNoDialog() } private const int maxHeight = 600; - private TaskCompletionSource task; + private TaskCompletionSource> task; private string defaultYes; private string defaultNo; } diff --git a/GUI/Main/Main.cs b/GUI/Main/Main.cs index 68575a4b38..ab00d401ca 100644 --- a/GUI/Main/Main.cs +++ b/GUI/Main/Main.cs @@ -13,6 +13,7 @@ using Autofac; using CKAN.Extensions; +using CKAN.Versioning; namespace CKAN { @@ -717,20 +718,36 @@ public void LaunchGame() return; var registry = RegistryManager.Instance(CurrentInstance).registry; - var incomp = registry.IncompatibleInstalled(CurrentInstance.VersionCriteria()) - .Where(m => !m.Module.IsDLC).ToList(); + + var suppressedIdentifiers = CurrentInstance.GetSuppressedCompatWarningIdentifiers; + var incomp = registry.IncompatibleInstalled(CurrentInstance.VersionCriteria()) + .Where(m => !m.Module.IsDLC && !suppressedIdentifiers.Contains(m.identifier)) + .ToList(); if (incomp.Any()) { // Warn that it might not be safe to run Game with incompatible modules installed string incompatDescrip = incomp .Select(m => $"{m.Module} ({registry.CompatibleGameVersions(CurrentInstance.game, m.Module)})") .Aggregate((a, b) => $"{a}{Environment.NewLine}{b}"); - if (!YesNoDialog(string.Format(Properties.Resources.MainLaunchWithIncompatible, incompatDescrip), + var ver = CurrentInstance.Version(); + var result = SuppressableYesNoDialog( + string.Format(Properties.Resources.MainLaunchWithIncompatible, incompatDescrip), + string.Format(Properties.Resources.MainLaunchDontShow, + CurrentInstance.game.ShortName, + new GameVersion(ver.Major, ver.Minor, ver.Patch)), Properties.Resources.MainLaunch, - Properties.Resources.MainGoBack)) + Properties.Resources.MainGoBack + ); + if (result.Item1 != DialogResult.Yes) { return; } + else if (result.Item2) + { + CurrentInstance.AddSuppressedCompatWarningIdentifiers( + incomp.Select(m => m.identifier).ToHashSet() + ); + } } split = CurrentInstance.game.AdjustCommandLine(split, diff --git a/GUI/Main/MainDialogs.cs b/GUI/Main/MainDialogs.cs index f786bfed24..16464247db 100644 --- a/GUI/Main/MainDialogs.cs +++ b/GUI/Main/MainDialogs.cs @@ -38,6 +38,16 @@ public bool YesNoDialog(string text, string yesText = null, string noText = null return yesNoDialog.ShowYesNoDialog(this, text, yesText, noText) == DialogResult.Yes; } + /// + /// Show a yes/no dialog with a "don't show again" checkbox + /// + /// A tuple of the dialog result and a bool indicating whether + /// the suppress-checkbox has been checked (true) + public Tuple SuppressableYesNoDialog(string text, string suppressText, string yesText = null, string noText = null) + { + return yesNoDialog.ShowSuppressableYesNoDialog(this, text, suppressText, yesText, noText); + } + public int SelectionDialog(string message, params object[] args) { return selectionDialog.ShowSelectionDialog(message, args); diff --git a/GUI/Properties/Resources.Designer.cs b/GUI/Properties/Resources.Designer.cs index 5d3d27db7e..90861ac628 100644 --- a/GUI/Properties/Resources.Designer.cs +++ b/GUI/Properties/Resources.Designer.cs @@ -408,6 +408,9 @@ internal static string MainFilterUntagged { internal static string MainLaunchWithIncompatible { get { return (string)(ResourceManager.GetObject("MainLaunchWithIncompatible", resourceCulture)); } } + internal static string MainLaunchDontShow { + get { return (string)(ResourceManager.GetObject("MainLaunchDontShow", resourceCulture)); } + } internal static string MainLaunch { get { return (string)(ResourceManager.GetObject("MainLaunch", resourceCulture)); } } diff --git a/GUI/Properties/Resources.de-DE.resx b/GUI/Properties/Resources.de-DE.resx index dd3e09be31..e73a55e9cc 100644 --- a/GUI/Properties/Resources.de-DE.resx +++ b/GUI/Properties/Resources.de-DE.resx @@ -173,6 +173,7 @@ Versuche {2} aus {3} zu verschieben und CKAN neu zu starten. {0} Starten + Für diese Mods und {0} {1} nicht mehr anzeigen Das Spiel konnte nicht gestartet werden. diff --git a/GUI/Properties/Resources.fr-FR.resx b/GUI/Properties/Resources.fr-FR.resx index feeacc1042..2c5fa8345a 100644 --- a/GUI/Properties/Resources.fr-FR.resx +++ b/GUI/Properties/Resources.fr-FR.resx @@ -172,6 +172,7 @@ Essayez de déplacer {2} en dehors de {3} et redémarrez CKAN. {0} Lancement + Ne plus demander pour ces mods sur {0} version {1} Impossible de démarrer le jeu. {0} diff --git a/GUI/Properties/Resources.resx b/GUI/Properties/Resources.resx index 2c78267e55..36bb4a59d3 100644 --- a/GUI/Properties/Resources.resx +++ b/GUI/Properties/Resources.resx @@ -192,6 +192,7 @@ Try to move {2} out of {3} and restart CKAN. Some installed modules are incompatible! It might not be safe to launch the game. Really launch? {0} + Don't show this again for these mods on {0} {1} Launch Couldn't start game.