From 851b3fb1c5cd64b66e4717300e1091e2d07e6a63 Mon Sep 17 00:00:00 2001 From: Paul Hebble Date: Tue, 6 Aug 2024 11:46:11 -0500 Subject: [PATCH 1/2] Support `suppress_recommendations` in modpacks --- Core/Relationships/RelationshipResolver.cs | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/Core/Relationships/RelationshipResolver.cs b/Core/Relationships/RelationshipResolver.cs index 8cf4d70a7..7a70154a2 100644 --- a/Core/Relationships/RelationshipResolver.cs +++ b/Core/Relationships/RelationshipResolver.cs @@ -229,6 +229,7 @@ private void ResolveStanza(List stanza, if (options.get_recommenders && descriptor.suppress_recommendations) { log.DebugFormat("Skipping {0} because get_recommenders option is set", descriptor.ToString()); + suppressedRecommenders.Add(descriptor); continue; } options = orig_options.OptionsFor(descriptor); @@ -575,18 +576,25 @@ public IEnumerable Dependencies() public IEnumerable Recommendations(HashSet dependencies) => modlist.Values.Except(dependencies) - .Where(m => ReasonsFor(m).Any(r => r is SelectionReason.Recommended - && dependencies.Contains(r.Parent))) + .Where(m => ValidRecSugReasons(dependencies, + ReasonsFor(m).Where(r => r is SelectionReason.Recommended) + .ToList())) .OrderByDescending(totalDependers); public IEnumerable Suggestions(HashSet dependencies, List recommendations) => modlist.Values.Except(dependencies) .Except(recommendations) - .Where(m => ReasonsFor(m).Any(r => r is SelectionReason.Suggested - && dependencies.Contains(r.Parent))) + .Where(m => ValidRecSugReasons(dependencies, + ReasonsFor(m).Where(r => r is SelectionReason.Suggested) + .ToList())) .OrderByDescending(totalDependers); + private bool ValidRecSugReasons(HashSet dependencies, + List recSugReasons) + => recSugReasons.Any(r => dependencies.Contains(r.Parent)) + && !suppressedRecommenders.Any(rel => recSugReasons.Any(r => rel.WithinBounds(r.Parent))); + public ParallelQuery>> Supporters( HashSet supported, IEnumerable toExclude) @@ -682,6 +690,12 @@ private void AddReason(CkanModule module, SelectionReason reason) private readonly Dictionary> reasons = new Dictionary>(); + /// + /// Depends relationships with suppress_recommendations=true, + /// to be applied to all recommendations and suggestions + /// + private HashSet suppressedRecommenders = new HashSet(); + private readonly IRegistryQuerier registry; private readonly GameVersionCriteria versionCrit; private readonly RelationshipResolverOptions options; From f8785f98c91d9728f69f00963a0b2c356a4b7716 Mon Sep 17 00:00:00 2001 From: Paul Hebble Date: Tue, 6 Aug 2024 12:31:10 -0500 Subject: [PATCH 2/2] Suppress recs checkbox for export modpack --- GUI/Controls/EditModpack.Designer.cs | 47 ++++++++++++++++++---------- GUI/Controls/EditModpack.cs | 45 +++++++++++++++++++++++--- GUI/Controls/EditModpack.resx | 1 + GUI/Controls/ModInfoTabs/Contents.cs | 2 +- GUI/Controls/UnmanagedFiles.cs | 2 +- GUI/Properties/Resources.resx | 2 ++ 6 files changed, 76 insertions(+), 23 deletions(-) diff --git a/GUI/Controls/EditModpack.Designer.cs b/GUI/Controls/EditModpack.Designer.cs index a80dac22f..dcb8381d6 100644 --- a/GUI/Controls/EditModpack.Designer.cs +++ b/GUI/Controls/EditModpack.Designer.cs @@ -48,6 +48,7 @@ private void InitializeComponent() this.LicenseLabel = new System.Windows.Forms.Label(); this.LicenseComboBox = new System.Windows.Forms.ComboBox(); this.IncludeVersionsCheckbox = new System.Windows.Forms.CheckBox(); + this.IncludeOptRelsCheckbox = new System.Windows.Forms.CheckBox(); this.RelationshipsListView = new CKAN.GUI.ThemedListView(); this.ModNameColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.ModVersionColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); @@ -91,6 +92,7 @@ private void InitializeComponent() this.TopEditPanel.Controls.Add(this.LicenseLabel); this.TopEditPanel.Controls.Add(this.LicenseComboBox); this.TopEditPanel.Controls.Add(this.IncludeVersionsCheckbox); + this.TopEditPanel.Controls.Add(this.IncludeOptRelsCheckbox); this.TopEditPanel.Dock = System.Windows.Forms.DockStyle.Top; this.TopEditPanel.Name = "TopEditPanel"; this.TopEditPanel.Size = new System.Drawing.Size(500, 160); @@ -166,7 +168,7 @@ private void InitializeComponent() this.AuthorLabel.Location = new System.Drawing.Point(10, 130); this.AuthorLabel.Name = "AuthorLabel"; this.AuthorLabel.Size = new System.Drawing.Size(75, 23); - this.AuthorLabel.TabIndex = 4; + this.AuthorLabel.TabIndex = 6; resources.ApplyResources(this.AuthorLabel, "AuthorLabel"); // // AuthorTextBox @@ -175,7 +177,7 @@ private void InitializeComponent() this.AuthorTextBox.Location = new System.Drawing.Point(125, 130); this.AuthorTextBox.Name = "AbstractTextBox"; this.AuthorTextBox.Size = new System.Drawing.Size(250, 23); - this.AuthorTextBox.TabIndex = 5; + this.AuthorTextBox.TabIndex = 7; this.AuthorTextBox.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend; this.AuthorTextBox.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.CustomSource; resources.ApplyResources(this.AuthorTextBox, "AuthorTextBox"); @@ -187,7 +189,7 @@ private void InitializeComponent() this.VersionLabel.Location = new System.Drawing.Point(400, 10); this.VersionLabel.Name = "VersionLabel"; this.VersionLabel.Size = new System.Drawing.Size(75, 23); - this.VersionLabel.TabIndex = 6; + this.VersionLabel.TabIndex = 8; resources.ApplyResources(this.VersionLabel, "VersionLabel"); // // VersionTextBox @@ -196,7 +198,7 @@ private void InitializeComponent() this.VersionTextBox.Location = new System.Drawing.Point(515, 10); this.VersionTextBox.Name = "VersionTextBox"; this.VersionTextBox.Size = new System.Drawing.Size(250, 23); - this.VersionTextBox.TabIndex = 7; + this.VersionTextBox.TabIndex = 9; this.VersionTextBox.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend; this.VersionTextBox.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.CustomSource; resources.ApplyResources(this.VersionTextBox, "VersionTextBox"); @@ -208,7 +210,7 @@ private void InitializeComponent() this.GameVersionLabel.Location = new System.Drawing.Point(400, 40); this.GameVersionLabel.Name = "GameVersionLabel"; this.GameVersionLabel.Size = new System.Drawing.Size(75, 23); - this.GameVersionLabel.TabIndex = 8; + this.GameVersionLabel.TabIndex = 10; resources.ApplyResources(this.GameVersionLabel, "GameVersionLabel"); // // GameVersionMinComboBox @@ -218,7 +220,7 @@ private void InitializeComponent() this.GameVersionMinComboBox.Location = new System.Drawing.Point(515, 40); this.GameVersionMinComboBox.Name = "GameVersionMinComboBox"; this.GameVersionMinComboBox.Size = new System.Drawing.Size(70, 23); - this.GameVersionMinComboBox.TabIndex = 9; + this.GameVersionMinComboBox.TabIndex = 11; resources.ApplyResources(this.GameVersionMinComboBox, "GameVersionMinComboBox"); // // GameVersionMaxComboBox @@ -228,7 +230,7 @@ private void InitializeComponent() this.GameVersionMaxComboBox.Location = new System.Drawing.Point(595, 40); this.GameVersionMaxComboBox.Name = "GameVersionMaxComboBox"; this.GameVersionMaxComboBox.Size = new System.Drawing.Size(70, 23); - this.GameVersionMaxComboBox.TabIndex = 10; + this.GameVersionMaxComboBox.TabIndex = 12; resources.ApplyResources(this.GameVersionMaxComboBox, "GameVersionMaxComboBox"); // // LicenseLabel @@ -238,7 +240,7 @@ private void InitializeComponent() this.LicenseLabel.Location = new System.Drawing.Point(400, 70); this.LicenseLabel.Name = "LicenseLabel"; this.LicenseLabel.Size = new System.Drawing.Size(75, 23); - this.LicenseLabel.TabIndex = 11; + this.LicenseLabel.TabIndex = 13; resources.ApplyResources(this.LicenseLabel, "LicenseLabel"); // // LicenseComboBox @@ -248,7 +250,7 @@ private void InitializeComponent() this.LicenseComboBox.Location = new System.Drawing.Point(515, 70); this.LicenseComboBox.Name = "LicenseComboBox"; this.LicenseComboBox.Size = new System.Drawing.Size(150, 23); - this.LicenseComboBox.TabIndex = 12; + this.LicenseComboBox.TabIndex = 14; resources.ApplyResources(this.LicenseComboBox, "LicenseComboBox"); // // IncludeVersionsCheckbox @@ -260,9 +262,21 @@ private void InitializeComponent() this.IncludeVersionsCheckbox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.IncludeVersionsCheckbox.Name = "IncludeVersionsCheckbox"; this.IncludeVersionsCheckbox.Size = new System.Drawing.Size(131, 24); - this.IncludeVersionsCheckbox.TabIndex = 13; + this.IncludeVersionsCheckbox.TabIndex = 15; resources.ApplyResources(this.IncludeVersionsCheckbox, "IncludeVersionsCheckbox"); // + // IncludeOptRelsCheckbox + // + this.IncludeOptRelsCheckbox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left))); + this.IncludeOptRelsCheckbox.AutoSize = true; + this.IncludeOptRelsCheckbox.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.IncludeOptRelsCheckbox.Location = new System.Drawing.Point(515, 130); + this.IncludeOptRelsCheckbox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.IncludeOptRelsCheckbox.Name = "IncludeOptRelsCheckbox"; + this.IncludeOptRelsCheckbox.Size = new System.Drawing.Size(131, 24); + this.IncludeOptRelsCheckbox.TabIndex = 16; + resources.ApplyResources(this.IncludeOptRelsCheckbox, "IncludeOptRelsCheckbox"); + // // RelationshipsListView // this.RelationshipsListView.Dock = System.Windows.Forms.DockStyle.Fill; @@ -277,7 +291,7 @@ private void InitializeComponent() this.RelationshipsListView.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.RelationshipsListView.Name = "RelationshipsListView"; this.RelationshipsListView.Size = new System.Drawing.Size(1510, 841); - this.RelationshipsListView.TabIndex = 14; + this.RelationshipsListView.TabIndex = 17; this.RelationshipsListView.UseCompatibleStateImageBehavior = false; this.RelationshipsListView.View = System.Windows.Forms.View.Details; this.RelationshipsListView.Groups.Add(this.DependsGroup); @@ -343,7 +357,7 @@ private void InitializeComponent() this.DependsRadioButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.DependsRadioButton.Name = "DependsRadioButton"; this.DependsRadioButton.Size = new System.Drawing.Size(112, 30); - this.DependsRadioButton.TabIndex = 16; + this.DependsRadioButton.TabIndex = 18; this.DependsRadioButton.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; this.DependsRadioButton.Click += new System.EventHandler(this.DependsRadioButton_CheckedChanged); resources.ApplyResources(this.DependsRadioButton, "DependsRadioButton"); @@ -354,7 +368,7 @@ private void InitializeComponent() this.RecommendsRadioButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.RecommendsRadioButton.Name = "RecommendsRadioButton"; this.RecommendsRadioButton.Size = new System.Drawing.Size(112, 30); - this.RecommendsRadioButton.TabIndex = 17; + this.RecommendsRadioButton.TabIndex = 19; this.RecommendsRadioButton.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; this.RecommendsRadioButton.Click += new System.EventHandler(this.RecommendsRadioButton_CheckedChanged); resources.ApplyResources(this.RecommendsRadioButton, "RecommendsRadioButton"); @@ -365,7 +379,7 @@ private void InitializeComponent() this.SuggestsRadioButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.SuggestsRadioButton.Name = "SuggestsRadioButton"; this.SuggestsRadioButton.Size = new System.Drawing.Size(112, 30); - this.SuggestsRadioButton.TabIndex = 18; + this.SuggestsRadioButton.TabIndex = 20; this.SuggestsRadioButton.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; this.SuggestsRadioButton.Click += new System.EventHandler(this.SuggestsRadioButton_CheckedChanged); resources.ApplyResources(this.SuggestsRadioButton, "SuggestsRadioButton"); @@ -376,7 +390,7 @@ private void InitializeComponent() this.IgnoreRadioButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.IgnoreRadioButton.Name = "IgnoreRadioButton"; this.IgnoreRadioButton.Size = new System.Drawing.Size(112, 30); - this.IgnoreRadioButton.TabIndex = 19; + this.IgnoreRadioButton.TabIndex = 21; this.IgnoreRadioButton.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; this.IgnoreRadioButton.Click += new System.EventHandler(this.IgnoreRadioButton_CheckedChanged); resources.ApplyResources(this.IgnoreRadioButton, "IgnoreRadioButton"); @@ -388,7 +402,7 @@ private void InitializeComponent() this.CancelExportButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.CancelExportButton.Name = "CancelExportButton"; this.CancelExportButton.Size = new System.Drawing.Size(112, 30); - this.CancelExportButton.TabIndex = 20; + this.CancelExportButton.TabIndex = 22; this.CancelExportButton.UseVisualStyleBackColor = true; this.CancelExportButton.Click += new System.EventHandler(this.CancelExportButton_Click); resources.ApplyResources(this.CancelExportButton, "CancelExportButton"); @@ -442,6 +456,7 @@ private void InitializeComponent() private System.Windows.Forms.Label LicenseLabel; private System.Windows.Forms.ComboBox LicenseComboBox; private System.Windows.Forms.CheckBox IncludeVersionsCheckbox; + private System.Windows.Forms.CheckBox IncludeOptRelsCheckbox; private System.Windows.Forms.ListView RelationshipsListView; private System.Windows.Forms.ColumnHeader ModNameColumn; private System.Windows.Forms.ColumnHeader ModVersionColumn; diff --git a/GUI/Controls/EditModpack.cs b/GUI/Controls/EditModpack.cs index d7c009a48..f7be67ae9 100644 --- a/GUI/Controls/EditModpack.cs +++ b/GUI/Controls/EditModpack.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -32,6 +33,7 @@ public EditModpack() ToolTip.SetToolTip(GameVersionMaxComboBox, Properties.Resources.EditModpackTooltipGameVersionMax); ToolTip.SetToolTip(LicenseComboBox, Properties.Resources.EditModpackTooltipLicense); ToolTip.SetToolTip(IncludeVersionsCheckbox, Properties.Resources.EditModpackTooltipIncludeVersions); + ToolTip.SetToolTip(IncludeOptRelsCheckbox, Properties.Resources.EditModpackTooltipIncludeOptRels); ToolTip.SetToolTip(DependsRadioButton, Properties.Resources.EditModpackTooltipDepends); ToolTip.SetToolTip(RecommendsRadioButton, Properties.Resources.EditModpackTooltipRecommends); ToolTip.SetToolTip(SuggestsRadioButton, Properties.Resources.EditModpackTooltipSuggests); @@ -322,7 +324,7 @@ private void ExportModpackButton_Click(object sender, EventArgs e) badField.Focus(); user.RaiseError(error); } - else if (TrySavePrompt(modpackExportOptions, out _, out string filename)) + else if (TrySavePrompt(modpackExportOptions, out string filename)) { if (module.depends.Count == 0) { @@ -336,11 +338,15 @@ private void ExportModpackButton_Click(object sender, EventArgs e) { module.suggests = null; } - CkanModule.ToFile(ApplyVersionsCheckbox(module), filename); + CkanModule.ToFile(ApplyCheckboxes(module), filename); + OpenFileBrowser(filename); task?.SetResult(true); } } + private CkanModule ApplyCheckboxes(CkanModule input) + => ApplyIncludeOptRelsCheckbox(ApplyVersionsCheckbox(input)); + private CkanModule ApplyVersionsCheckbox(CkanModule input) { if (IncludeVersionsCheckbox.Checked) @@ -375,7 +381,38 @@ private CkanModule ApplyVersionsCheckbox(CkanModule input) } } - private bool TrySavePrompt(List exportOptions, out ExportOption selectedOption, out string filename) + private CkanModule ApplyIncludeOptRelsCheckbox(CkanModule input) + { + if (IncludeOptRelsCheckbox.Checked) + { + return input; + } + else + { + var newMod = CkanModule.FromJson(CkanModule.ToJson(input)); + foreach (var rel in newMod.depends) + { + rel.suppress_recommendations = true; + } + return newMod; + } + } + + private void OpenFileBrowser(string location) + { + if (File.Exists(location)) + { + // We need the folder of the file + // Otherwise the OS would try to open the file in its default application + location = Path.GetDirectoryName(location); + } + if (Directory.Exists(location)) + { + Utilities.ProcessStartURL(location); + } + } + + private bool TrySavePrompt(List exportOptions, out string filename) { var dlg = new SaveFileDialog() { @@ -384,13 +421,11 @@ private bool TrySavePrompt(List exportOptions, out ExportOption se }; if (dlg.ShowDialog(ParentForm) == DialogResult.OK) { - selectedOption = exportOptions[dlg.FilterIndex - 1]; filename = dlg.FileName; return true; } else { - selectedOption = null; filename = null; return false; } diff --git a/GUI/Controls/EditModpack.resx b/GUI/Controls/EditModpack.resx index 3d654bf49..c1b0a5dff 100644 --- a/GUI/Controls/EditModpack.resx +++ b/GUI/Controls/EditModpack.resx @@ -125,6 +125,7 @@ Game versions: Licence: Save mod versions + Include optional relationships Mod Version Description diff --git a/GUI/Controls/ModInfoTabs/Contents.cs b/GUI/Controls/ModInfoTabs/Contents.cs index 18ccd3814..8ea9351a0 100644 --- a/GUI/Controls/ModInfoTabs/Contents.cs +++ b/GUI/Controls/ModInfoTabs/Contents.cs @@ -179,7 +179,7 @@ private void OpenFileBrowser(TreeNode node) if (!Directory.Exists(location)) { // User either selected the parent node - // or he clicked on the tree node of a cached, but not installed mod + // or clicked on the tree node of a cached, but not installed mod return; } diff --git a/GUI/Controls/UnmanagedFiles.cs b/GUI/Controls/UnmanagedFiles.cs index cc1952827..128223e58 100644 --- a/GUI/Controls/UnmanagedFiles.cs +++ b/GUI/Controls/UnmanagedFiles.cs @@ -221,7 +221,7 @@ private void OpenFileBrowser(TreeNode node) if (!Directory.Exists(location)) { // User either selected the parent node - // or he clicked on the tree node of a cached, but not installed mod + // or clicked on the tree node of a cached, but not installed mod return; } diff --git a/GUI/Properties/Resources.resx b/GUI/Properties/Resources.resx index 1a2abb16e..31272cca7 100644 --- a/GUI/Properties/Resources.resx +++ b/GUI/Properties/Resources.resx @@ -421,6 +421,8 @@ Are you sure you want to skip this change? Latest compatible game version, blank for all The licence for this modpack If checked, the modpack will include the specific versions of the mods shown, otherwise any version will do + If checked, users installing this modpack will be shown recommendations and suggestions from mods in the pack. +If unchecked, only mods in the pack will be installed. Move selected mods to the Depends group. These mods will definitely be installed. Move selected mods to the Recommends group. A user can drop these mods on installation. Move selected mods to the Suggests group. These mods will be available for installation, but you have to explicitly select them.