diff --git a/Core/Properties/Resources.Designer.cs b/Core/Properties/Resources.Designer.cs
index 9108f6ff04..eecfc0cad8 100644
--- a/Core/Properties/Resources.Designer.cs
+++ b/Core/Properties/Resources.Designer.cs
@@ -591,5 +591,9 @@ internal static string SanityCheckerUnsatisfiedDependency {
internal static string SanityCheckerConflictsWith {
get { return (string)(ResourceManager.GetObject("SanityCheckerConflictsWith", resourceCulture)); }
}
+
+ internal static string ModpackName {
+ get { return (string)(ResourceManager.GetObject("ModpackName", resourceCulture)); }
+ }
}
}
diff --git a/Core/Properties/Resources.resx b/Core/Properties/Resources.resx
index 5112d9c76d..380a647877 100644
--- a/Core/Properties/Resources.resx
+++ b/Core/Properties/Resources.resx
@@ -308,4 +308,5 @@ Free up space on that device or change your settings to use another location.
{0} {1} ({2}, {3} remaining)
* Install: {0} {1} ({2}, {3} remaining)
* Upgrade: {0} {1} to {2} ({3}, {4} remaining)
+ installed-{0}
diff --git a/Core/Registry/RegistryManager.cs b/Core/Registry/RegistryManager.cs
index 871c61da9b..22f2661353 100644
--- a/Core/Registry/RegistryManager.cs
+++ b/Core/Registry/RegistryManager.cs
@@ -5,6 +5,8 @@
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
+using System.ComponentModel;
+using System.Reflection;
using ChinhDo.Transactions.FileManager;
using log4net;
@@ -462,7 +464,9 @@ private string SerializeCurrentInstall(bool recommends = false, bool with_versio
public CkanModule GenerateModpack(bool recommends = false, bool with_versions = true)
{
string gameInstanceName = gameInstance.Name;
- string name = $"installed-{gameInstanceName}";
+ string name = string.Format(Properties.Resources.ModpackName, gameInstanceName);
+ var crit = gameInstance.VersionCriteria();
+ var minAndMax = crit.MinAndMax;
var module = new CkanModule(
// v1.18 to allow Unlicense
new ModuleVersion("v1.18"),
@@ -470,51 +474,67 @@ public CkanModule GenerateModpack(bool recommends = false, bool with_versions =
name,
string.Format(Properties.Resources.RegistryManagerDefaultModpackAbstract, gameInstanceName),
null,
- new List() { Environment.UserName },
- new List() { new License("unknown") },
+ new List() { Environment.UserName },
+ new List() { License.UnknownLicense },
new ModuleVersion(DateTime.UtcNow.ToString("yyyy.MM.dd.hh.mm.ss")),
null,
- "metapackage"
- )
- {
- download_content_type = "application/zip",
- release_date = DateTime.Now,
+ "metapackage")
+ {
+ ksp_version_min = minAndMax.Lower.AsInclusiveLower().WithoutBuild,
+ ksp_version_max = minAndMax.Upper.AsInclusiveUpper().WithoutBuild,
+ download_content_type = typeof(CkanModule).GetTypeInfo()
+ .GetDeclaredField("download_content_type")
+ .GetCustomAttribute()
+ .Value.ToString(),
+ release_date = DateTime.Now,
};
- List mods = registry.Installed(false, false)
- .Where(kvp => {
- // Skip unavailable modules (custom .ckan files)
- try
- {
- var avail = registry.LatestAvailable(kvp.Key, null, null);
- return !avail.IsDLC;
- }
- catch
- {
- return false;
- }
- })
- // Case insensitive sort by identifier
- .OrderBy(kvp => kvp.Key, StringComparer.OrdinalIgnoreCase)
- .Select(kvp => (RelationshipDescriptor) new ModuleRelationshipDescriptor()
- {
- name = kvp.Key,
- version = with_versions ? kvp.Value : null
- })
+ var rels = registry.InstalledModules
+ .Where(inst => !inst.Module.IsDLC && IsAvailable(inst))
+ .OrderBy(inst => inst.identifier, StringComparer.OrdinalIgnoreCase)
+ .Select(with_versions ? (Func) RelationshipWithVersion
+ : RelationshipWithoutVersion)
.ToList();
if (recommends)
{
- module.recommends = mods;
+ module.recommends = rels;
}
else
{
- module.depends = mods;
+ module.depends = rels;
}
return module;
}
+ private bool IsAvailable(InstalledModule inst)
+ {
+ try
+ {
+ var avail = registry.LatestAvailable(inst.identifier, null, null);
+ return true;
+ }
+ catch
+ {
+ // Skip unavailable modules (custom .ckan files)
+ return false;
+ }
+ }
+
+ private RelationshipDescriptor RelationshipWithVersion(InstalledModule inst)
+ => new ModuleRelationshipDescriptor()
+ {
+ name = inst.identifier,
+ version = inst.Module.version,
+ };
+
+ private RelationshipDescriptor RelationshipWithoutVersion(InstalledModule inst)
+ => new ModuleRelationshipDescriptor()
+ {
+ name = inst.identifier,
+ };
+
///
/// Look for DLC installed in GameData
///
diff --git a/Core/Relationships/RelationshipResolver.cs b/Core/Relationships/RelationshipResolver.cs
index a313cd130b..9361108ee7 100644
--- a/Core/Relationships/RelationshipResolver.cs
+++ b/Core/Relationships/RelationshipResolver.cs
@@ -567,7 +567,10 @@ private void ResolveStanza(IEnumerable stanza, Selection
private void Add(CkanModule module, SelectionReason reason)
{
if (module.IsMetapackage)
+ {
+ AddReason(module, reason);
return;
+ }
if (module.IsDLC)
{
throw new ModuleIsDLCKraken(module);
@@ -698,6 +701,12 @@ public IEnumerable ModList()
log.DebugFormat("Parent found: {0}, {1}", index, module);
sortedDepsFirst.Insert(index, module);
}
+ catch (ArgumentException)
+ {
+ // ReasonsFor throws this for mods without reasons, just add it to the end
+ log.DebugFormat("Reasons for parent not found: {0}", module);
+ sortedDepsFirst.Add(module);
+ }
catch (InvalidOperationException)
{
// No index, just append
diff --git a/Core/Types/RelationshipDescriptor.cs b/Core/Types/RelationshipDescriptor.cs
index 7c7d4c2b28..34693a47e4 100644
--- a/Core/Types/RelationshipDescriptor.cs
+++ b/Core/Types/RelationshipDescriptor.cs
@@ -29,8 +29,11 @@ out CkanModule matched
public abstract List LatestAvailableWithProvides(
IRegistryQuerier registry, GameVersionCriteria crit, IEnumerable installed = null,
- IEnumerable toInstall = null
- );
+ IEnumerable toInstall = null);
+
+ public abstract CkanModule ExactMatch(
+ IRegistryQuerier registry, GameVersionCriteria crit, IEnumerable installed = null,
+ IEnumerable toInstall = null);
public abstract bool Equals(RelationshipDescriptor other);
@@ -167,11 +170,14 @@ out CkanModule matched
public override List LatestAvailableWithProvides(
IRegistryQuerier registry, GameVersionCriteria crit, IEnumerable installed = null,
- IEnumerable toInstall = null
- )
- {
- return registry.LatestAvailableWithProvides(name, crit, this, installed, toInstall);
- }
+ IEnumerable toInstall = null)
+ => registry.LatestAvailableWithProvides(name, crit, this, installed, toInstall);
+
+ public override CkanModule ExactMatch(
+ IRegistryQuerier registry, GameVersionCriteria crit, IEnumerable installed = null,
+ IEnumerable toInstall = null)
+ => registry.LatestAvailableWithProvides(name, crit, this, installed, toInstall)
+ .FirstOrDefault(mod => mod.identifier == name);
public override bool Equals(RelationshipDescriptor other)
{
@@ -183,15 +189,10 @@ public override bool Equals(RelationshipDescriptor other)
&& max_version == modRel.max_version;
}
- public override bool ContainsAny(IEnumerable identifiers)
- {
- return identifiers.Contains(name);
- }
+ public override bool ContainsAny(IEnumerable identifiers) => identifiers.Contains(name);
public override bool StartsWith(string prefix)
- {
- return name.IndexOf(prefix, StringComparison.CurrentCultureIgnoreCase) == 0;
- }
+ => name.IndexOf(prefix, StringComparison.CurrentCultureIgnoreCase) == 0;
///
/// Generate a user readable description of the relationship
@@ -205,16 +206,13 @@ public override bool StartsWith(string prefix)
/// name max_version or earlier
///
public override string ToString()
- {
- return
- version != null ? $"{name} {version}"
+ => version != null ? $"{name} {version}"
: min_version != null && max_version != null ? $"{name} {min_version}–{max_version}"
: min_version != null
? string.Format(Properties.Resources.RelationshipDescriptorMinVersionOnly, name, min_version)
: max_version != null
? string.Format(Properties.Resources.RelationshipDescriptorMaxVersionOnly, name, max_version)
: name;
- }
}
@@ -262,11 +260,14 @@ out CkanModule matched
public override List LatestAvailableWithProvides(
IRegistryQuerier registry, GameVersionCriteria crit, IEnumerable installed = null,
- IEnumerable toInstall = null
- )
- {
- return any_of?.SelectMany(r => r.LatestAvailableWithProvides(registry, crit, installed, toInstall)).Distinct().ToList();
- }
+ IEnumerable toInstall = null)
+ => any_of?.SelectMany(r => r.LatestAvailableWithProvides(registry, crit, installed, toInstall)).Distinct().ToList();
+
+ // Exact match is not possible for any_of
+ public override CkanModule ExactMatch(
+ IRegistryQuerier registry, GameVersionCriteria crit, IEnumerable installed = null,
+ IEnumerable toInstall = null)
+ => null;
public override bool Equals(RelationshipDescriptor other)
{
@@ -276,22 +277,14 @@ public override bool Equals(RelationshipDescriptor other)
}
public override bool ContainsAny(IEnumerable identifiers)
- {
- return any_of?.Any(r => r.ContainsAny(identifiers))
- ?? false;
- }
+ => any_of?.Any(r => r.ContainsAny(identifiers)) ?? false;
public override bool StartsWith(string prefix)
- {
- return any_of?.Any(r => r.StartsWith(prefix))
- ?? false;
- }
+ => any_of?.Any(r => r.StartsWith(prefix)) ?? false;
public override string ToString()
- {
- return any_of?.Select(r => r.ToString())
+ => any_of?.Select(r => r.ToString())
.Aggregate((a, b) =>
string.Format(Properties.Resources.RelationshipDescriptorAnyOfJoiner, a, b));
- }
}
}
diff --git a/Core/Versioning/GameVersion.cs b/Core/Versioning/GameVersion.cs
index 192cb8e795..c7976a742b 100644
--- a/Core/Versioning/GameVersion.cs
+++ b/Core/Versioning/GameVersion.cs
@@ -36,65 +36,59 @@ public sealed partial class GameVersion
/// Gets the value of the major component of the version number for the current
/// object.
///
- public int Major { get { return _major; } }
+ public int Major => _major;
///
/// Gets the value of the minor component of the version number for the current
/// object.
///
- public int Minor { get { return _minor; } }
+ public int Minor => _minor;
///
/// Gets the value of the patch component of the version number for the current
/// object.
///
- public int Patch { get { return _patch; } }
+ public int Patch => _patch;
///
/// Gets the value of the build component of the version number for the current
/// object.
///
- public int Build { get { return _build; } }
+ public int Build => _build;
///
/// Gets whether or not the major component of the version number for the current
/// object is defined.
///
- public bool IsMajorDefined { get { return _major != Undefined; } }
+ public bool IsMajorDefined => _major != Undefined;
///
/// Gets whether or not the minor component of the version number for the current
/// object is defined.
///
- public bool IsMinorDefined { get { return _minor != Undefined; } }
+ public bool IsMinorDefined => _minor != Undefined;
///
/// Gets whether or not the patch component of the version number for the current
/// object is defined.
///
- public bool IsPatchDefined { get { return _patch != Undefined; } }
+ public bool IsPatchDefined => _patch != Undefined;
///
/// Gets whether or not the build component of the version number for the current
/// object is defined.
///
- public bool IsBuildDefined { get { return _build != Undefined; } }
+ public bool IsBuildDefined => _build != Undefined;
///
/// Indicates whether or not all components of the current are defined.
///
- public bool IsFullyDefined
- {
- get { return IsMajorDefined && IsMinorDefined && IsPatchDefined && IsBuildDefined; }
- }
+ public bool IsFullyDefined => IsMajorDefined && IsMinorDefined && IsPatchDefined && IsBuildDefined;
///
/// Indicates wheter or not all the components of the current are undefined.
///
- public bool IsAny
- {
- get { return !IsMajorDefined && !IsMinorDefined && !IsPatchDefined && !IsBuildDefined; }
- }
+ public bool IsAny => !IsMajorDefined && !IsMinorDefined && !IsPatchDefined && !IsBuildDefined;
///
/// Check whether a version is null or Any.
@@ -104,10 +98,7 @@ public bool IsAny
///
/// True if null or Any, false otherwise
///
- public static bool IsNullOrAny(GameVersion v)
- {
- return v == null || v.IsAny;
- }
+ public static bool IsNullOrAny(GameVersion v) => v == null || v.IsAny;
///
/// Initialize a new instance of the class with all components unspecified.
@@ -240,10 +231,7 @@ public GameVersion(int major, int minor, int patch, int build)
/// If the current is totally undefined the return value will null.
///
///
- public override string ToString()
- {
- return _string;
- }
+ public override string ToString() => _string;
private static Dictionary VersionsMax = new Dictionary();
@@ -304,6 +292,13 @@ private static int UptoNines(int num)
return (int)Math.Pow(10, Math.Floor(Math.Log10(num + 1)) + 1) - 1;
}
+ ///
+ /// Strip off the build number if it's defined
+ ///
+ /// A GameVersion equal to this but without a build number
+ public GameVersion WithoutBuild => IsBuildDefined ? new GameVersion(_major, _minor, _patch)
+ : this;
+
///
/// Converts the value of the current to its equivalent
/// .
@@ -491,7 +486,7 @@ public bool InBuildMap(IGame game)
/// Raises a selection dialog for choosing a specific KSP version, if it is not fully defined yet.
/// If a build number is specified but not known, it presents a list of all builds
/// of the patch range.
- /// Needs at least a Major and Minor (doesn't make sense else).
+ /// Needs at least a Major and Minor (doesn't make sense else).
///
/// A complete GameVersion object
/// A IUser instance, to raise the corresponding dialog.
@@ -526,7 +521,7 @@ public GameVersion RaiseVersionSelectionDialog(IGame game, IUser user)
{
possibleVersions.Add(ver);
}
- }
+ }
// If we also have Patch -> compare it too.
else if (!IsBuildDefined)
{
diff --git a/Core/Versioning/GameVersionBound.cs b/Core/Versioning/GameVersionBound.cs
index 4c48d20f6d..9ddbd86dcd 100644
--- a/Core/Versioning/GameVersionBound.cs
+++ b/Core/Versioning/GameVersionBound.cs
@@ -33,10 +33,35 @@ public GameVersionBound(GameVersion value, bool inclusive)
_string = inclusive ? string.Format("[{0}]", valueStr) : string.Format("({0})", valueStr);
}
- public override string ToString()
- {
- return _string;
- }
+ public GameVersion AsInclusiveLower()
+ => Inclusive
+ // Already inclusive? Drop all trailing 0 values
+ ? (Value.IsPatchDefined && Value.Patch > 0 ? new GameVersion(Value.Major, Value.Minor, Value.Patch)
+ : Value.IsMinorDefined && Value.Minor > 0 ? new GameVersion(Value.Major, Value.Minor)
+ : Value.IsMajorDefined && Value.Major > 0 ? new GameVersion(Value.Major)
+ : GameVersion.Any)
+ // 1.3.1 non-inclusive => 1.3.2 inclusive lower
+ : Value.IsPatchDefined ? new GameVersion(Value.Major, Value.Minor, Value.Patch + 1)
+ // 1.3 non-inclusive => 1.4 inclusive lower
+ : Value.IsMinorDefined ? new GameVersion(Value.Major, Value.Minor + 1)
+ // 1 non-inclusive => 2 inclusive lower
+ : Value.IsMajorDefined ? new GameVersion(Value.Major + 1)
+ // Unbounded, I guess?
+ : GameVersion.Any;
+
+ public GameVersion AsInclusiveUpper()
+ // Already inclusive?
+ => Inclusive ? Value
+ // 1.3.1 non-inclusive => 1.3.0 inclusive upper
+ : Value.IsPatchDefined && Value.Patch > 0 ? new GameVersion(Value.Major, Value.Minor, Value.Patch - 1)
+ // 1.3 non-inclusive => 1.2 inclusive upper
+ : Value.IsMinorDefined && Value.Minor > 0 ? new GameVersion(Value.Major, Value.Minor - 1)
+ // 2 non-inclusive => 1 inclusive lower
+ : Value.IsMajorDefined && Value.Major > 0 ? new GameVersion(Value.Major - 1)
+ // Unbounded, I guess?
+ : GameVersion.Any;
+
+ public override string ToString() => _string;
}
public sealed partial class GameVersionBound : IEquatable
@@ -63,15 +88,8 @@ public override int GetHashCode()
}
}
- public static bool operator ==(GameVersionBound left, GameVersionBound right)
- {
- return Equals(left, right);
- }
-
- public static bool operator !=(GameVersionBound left, GameVersionBound right)
- {
- return !Equals(left, right);
- }
+ public static bool operator ==(GameVersionBound left, GameVersionBound right) => Equals(left, right);
+ public static bool operator !=(GameVersionBound left, GameVersionBound right) => !Equals(left, right);
}
public sealed partial class GameVersionBound
diff --git a/Core/Versioning/GameVersionCriteria.cs b/Core/Versioning/GameVersionCriteria.cs
index e33ead8c76..890964dfee 100644
--- a/Core/Versioning/GameVersionCriteria.cs
+++ b/Core/Versioning/GameVersionCriteria.cs
@@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.Linq;
+using CKAN.Games;
+
namespace CKAN.Versioning
{
public class GameVersionCriteria : IEquatable
@@ -26,49 +28,36 @@ public GameVersionCriteria(GameVersion v, List compatibleVersions)
this._versions = this._versions.Distinct().ToList();
}
- public IList Versions
- {
- get
- {
- return _versions.AsReadOnly();
- }
- }
+ public IList Versions => _versions.AsReadOnly();
+
+ public GameVersionRange MinAndMax => Versions
+ .Skip(1)
+ .Select(v => v.ToVersionRange())
+ .Aggregate(Versions.First().ToVersionRange(),
+ (range, v) => new GameVersionRange(
+ GameVersionBound.Lowest(range.Lower, v.Lower),
+ GameVersionBound.Highest(range.Upper, v.Upper)));
public GameVersionCriteria Union(GameVersionCriteria other)
- {
- return new GameVersionCriteria(
- null,
- _versions.Union(other.Versions).ToList()
- );
- }
+ => new GameVersionCriteria(null, _versions.Union(other.Versions).ToList());
public override bool Equals(object obj)
- {
- return Equals(obj as GameVersionCriteria);
- }
+ => Equals(obj as GameVersionCriteria);
// From IEquatable
public bool Equals(GameVersionCriteria other)
- {
- return other == null
- ? false
- : !_versions.Except(other._versions).Any()
- && !other._versions.Except(_versions).Any();
- }
+ => other == null ? false
+ : !_versions.Except(other._versions).Any()
+ && !other._versions.Except(_versions).Any();
public override int GetHashCode()
- {
- return _versions.Aggregate(19, (code, vers) => code * 31 + vers.GetHashCode());
- }
+ => _versions.Aggregate(19, (code, vers) => code * 31 + vers.GetHashCode());
public override String ToString()
- {
- List versionList = new List();
- foreach (GameVersion version in _versions)
- {
- versionList.Add(version.ToString());
- }
- return string.Format(Properties.Resources.GameVersionCriteriaToString, string.Join( ", ", versionList));
- }
+ => string.Format(Properties.Resources.GameVersionCriteriaToString,
+ string.Join(", ", _versions.Select(v => v.ToString())));
+
+ public string ToSummaryString(IGame game)
+ => MinAndMax.ToSummaryString(game);
}
}
diff --git a/Core/Versioning/GameVersionRange.cs b/Core/Versioning/GameVersionRange.cs
index 74ce3d94ad..21feb44b14 100644
--- a/Core/Versioning/GameVersionRange.cs
+++ b/Core/Versioning/GameVersionRange.cs
@@ -12,7 +12,7 @@ public sealed partial class GameVersionRange
new GameVersionRange(GameVersionBound.Unbounded, GameVersionBound.Unbounded);
public GameVersionBound Lower { get; private set; }
- public GameVersionBound Upper { get; private set; }
+ public GameVersionBound Upper { get; private set; }
public GameVersionRange(GameVersionBound lower, GameVersionBound upper)
{
@@ -31,10 +31,7 @@ public GameVersionRange(GameVersionBound lower, GameVersionBound upper)
public GameVersionRange(GameVersion lower, GameVersion upper)
: this(lower?.ToVersionRange().Lower, upper?.ToVersionRange().Upper) { }
- public override string ToString()
- {
- return _string;
- }
+ public override string ToString() =>_string;
public GameVersionRange IntersectWith(GameVersionRange other)
{
@@ -64,10 +61,8 @@ public bool IsSupersetOf(GameVersionRange other)
}
private static bool IsEmpty(GameVersionBound lower, GameVersionBound upper)
- {
- return upper.Value < lower.Value ||
+ => upper.Value < lower.Value ||
(lower.Value == upper.Value && (!lower.Inclusive || !upper.Inclusive));
- }
private static string DeriveString(GameVersionRange versionRange)
{
@@ -89,11 +84,9 @@ private static string DeriveString(GameVersionRange versionRange)
}
private static string SameVersionString(GameVersion v)
- {
- return v == null ? "???"
- : v.IsAny ? "all versions"
- : v.ToString();
- }
+ => v == null ? "???"
+ : v.IsAny ? Properties.Resources.CkanModuleAllVersions
+ : v.ToString();
///
/// Generate a string describing a range of KSP versions.
@@ -105,15 +98,18 @@ private static string SameVersionString(GameVersion v)
/// Human readable string describing the versions.
///
public static string VersionSpan(IGame game, GameVersion minKsp, GameVersion maxKsp)
- {
- return minKsp == maxKsp ? $"{game.ShortName} {SameVersionString(minKsp)}"
- : minKsp.IsAny
+ => minKsp == maxKsp
+ ? $"{game.ShortName} {SameVersionString(minKsp)}"
+ : minKsp.IsAny
? string.Format(Properties.Resources.GameVersionRangeMinOnly, game.ShortName, maxKsp)
- : maxKsp.IsAny
- ? string.Format(Properties.Resources.GameVersionRangeMaxOnly, game.ShortName, minKsp)
- : $"{game.ShortName} {minKsp}–{maxKsp}";
- }
-
+ : maxKsp.IsAny
+ ? string.Format(Properties.Resources.GameVersionRangeMaxOnly, game.ShortName, minKsp)
+ : $"{game.ShortName} {minKsp}–{maxKsp}";
+
+ public string ToSummaryString(IGame game)
+ => VersionSpan(game,
+ Lower.AsInclusiveLower().WithoutBuild,
+ Upper.AsInclusiveUpper().WithoutBuild);
}
public sealed partial class GameVersionRange : IEquatable
@@ -140,14 +136,7 @@ public override int GetHashCode()
}
}
- public static bool operator ==(GameVersionRange left, GameVersionRange right)
- {
- return Equals(left, right);
- }
-
- public static bool operator !=(GameVersionRange left, GameVersionRange right)
- {
- return !Equals(left, right);
- }
+ public static bool operator ==(GameVersionRange left, GameVersionRange right) => Equals(left, right);
+ public static bool operator !=(GameVersionRange left, GameVersionRange right) => !Equals(left, right);
}
}
diff --git a/GUI/Controls/EditModpack.cs b/GUI/Controls/EditModpack.cs
index fa5ca963b3..5738640eff 100644
--- a/GUI/Controls/EditModpack.cs
+++ b/GUI/Controls/EditModpack.cs
@@ -317,6 +317,18 @@ private void ExportModpackButton_Click(object sender, EventArgs e)
}
else if (TrySavePrompt(modpackExportOptions, out ExportOption selectedOption, out string filename))
{
+ if (module.depends.Count == 0)
+ {
+ module.depends = null;
+ }
+ if (module.recommends.Count == 0)
+ {
+ module.recommends = null;
+ }
+ if (module.suggests.Count == 0)
+ {
+ module.suggests = null;
+ }
CkanModule.ToFile(ApplyVersionsCheckbox(module), filename);
task?.SetResult(true);
}
diff --git a/GUI/Controls/ModInfoTabs/Versions.cs b/GUI/Controls/ModInfoTabs/Versions.cs
index 8a4a567678..882b577956 100644
--- a/GUI/Controls/ModInfoTabs/Versions.cs
+++ b/GUI/Controls/ModInfoTabs/Versions.cs
@@ -70,7 +70,9 @@ private bool allowInstall(CkanModule module)
return installable(installer, module, registry)
|| Main.Instance.YesNoDialog(
- string.Format(Properties.Resources.AllModVersionsInstallPrompt, module.ToString()),
+ string.Format(Properties.Resources.AllModVersionsInstallPrompt,
+ module.ToString(),
+ currentInstance.VersionCriteria().ToSummaryString(currentInstance.game)),
Properties.Resources.AllModVersionsInstallYes,
Properties.Resources.AllModVersionsInstallNo);
}
diff --git a/GUI/Controls/Wait.cs b/GUI/Controls/Wait.cs
index 6f9780c1dc..2d975275bc 100644
--- a/GUI/Controls/Wait.cs
+++ b/GUI/Controls/Wait.cs
@@ -85,6 +85,7 @@ public void SetProgress(string label, long remaining, long total)
{
AutoSize = true,
Text = label,
+ Margin = new Padding(0, 8, 0, 0),
};
progressLabels.Add(label, newLb);
var newPb = new ProgressBar()
diff --git a/GUI/Main/Main.cs b/GUI/Main/Main.cs
index 4d2c88fbe6..b1c3a6f645 100644
--- a/GUI/Main/Main.cs
+++ b/GUI/Main/Main.cs
@@ -563,6 +563,10 @@ private void installFromckanToolStripMenuItem_Click(object sender, EventArgs e)
{
// We'll need to make some registry changes to do this.
RegistryManager registry_manager = RegistryManager.Instance(CurrentInstance);
+ var crit = CurrentInstance.VersionCriteria();
+
+ var installed = registry_manager.registry.InstalledModules.Select(inst => inst.Module).ToList();
+ var toInstall = new List();
foreach (string path in open_file_dialog.FileNames)
{
@@ -571,6 +575,20 @@ private void installFromckanToolStripMenuItem_Click(object sender, EventArgs e)
try
{
module = CkanModule.FromFile(path);
+ if (module.IsMetapackage && module.depends != null)
+ {
+ // Add metapackage dependencies to the changeset so we can skip compat checks for them
+ toInstall.AddRange(module.depends
+ .Where(rel => !rel.MatchesAny(installed, null, null))
+ .Select(rel =>
+ // If there's a compatible match, return it
+ // Metapackages aren't intending to prompt users to choose providing mods
+ rel.ExactMatch(registry_manager.registry, crit, installed, toInstall)
+ // Otherwise look for incompatible
+ ?? rel.ExactMatch(registry_manager.registry, null, installed, toInstall))
+ .Where(mod => mod != null));
+ }
+ toInstall.Add(module);
}
catch (Kraken kraken)
{
@@ -591,10 +609,24 @@ private void installFromckanToolStripMenuItem_Click(object sender, EventArgs e)
currentUser.RaiseError(Properties.Resources.MainCantInstallDLC, module);
continue;
}
-
- InstallModuleDriver(registry_manager.registry, module);
}
- registry_manager.Save(true);
+ // Get all recursively incompatible module identifiers (quickly)
+ var allIncompat = registry_manager.registry.IncompatibleModules(crit)
+ .Select(mod => mod.identifier)
+ .ToHashSet();
+ // Get incompatible mods we're installing
+ var myIncompat = toInstall.Where(mod => allIncompat.Contains(mod.identifier)).ToList();
+ if (!myIncompat.Any()
+ // Confirm installation of incompatible like the Versions tab does
+ || Main.Instance.YesNoDialog(
+ string.Format(Properties.Resources.ModpackInstallIncompatiblePrompt,
+ string.Join(Environment.NewLine, myIncompat),
+ crit.ToSummaryString(CurrentInstance.game)),
+ Properties.Resources.AllModVersionsInstallYes,
+ Properties.Resources.AllModVersionsInstallNo))
+ {
+ InstallModuleDriver(registry_manager.registry, toInstall);
+ }
}
}
diff --git a/GUI/Main/MainInstall.cs b/GUI/Main/MainInstall.cs
index 309801b2ab..6e3566aab4 100644
--- a/GUI/Main/MainInstall.cs
+++ b/GUI/Main/MainInstall.cs
@@ -17,29 +17,30 @@ public partial class Main
///
/// Reference to the registry
/// Module to install
- public void InstallModuleDriver(IRegistryQuerier registry, CkanModule module)
+ public void InstallModuleDriver(IRegistryQuerier registry, IEnumerable modules)
{
try
{
DisableMainWindow();
var userChangeSet = new List();
- InstalledModule installed = registry.InstalledModule(module.identifier);
- if (installed != null)
+ foreach (var module in modules)
{
- // Already installed, remove it first
- userChangeSet.Add(new ModChange(installed.Module, GUIModChangeType.Remove));
+ InstalledModule installed = registry.InstalledModule(module.identifier);
+ if (installed != null)
+ {
+ // Already installed, remove it first
+ userChangeSet.Add(new ModChange(installed.Module, GUIModChangeType.Remove));
+ }
+ // Install the selected mod
+ userChangeSet.Add(new ModChange(module, GUIModChangeType.Install));
}
- // Install the selected mod
- userChangeSet.Add(new ModChange(module, GUIModChangeType.Install));
if (userChangeSet.Count > 0)
{
// Resolve the provides relationships in the dependencies
Wait.StartWaiting(InstallMods, PostInstallMods, true,
new KeyValuePair, RelationshipResolverOptions>(
userChangeSet,
- RelationshipResolver.DependsOnlyOpts()
- )
- );
+ RelationshipResolver.DependsOnlyOpts()));
}
}
catch
diff --git a/GUI/Properties/Resources.Designer.cs b/GUI/Properties/Resources.Designer.cs
index 5034c41f55..7697cd7e39 100644
--- a/GUI/Properties/Resources.Designer.cs
+++ b/GUI/Properties/Resources.Designer.cs
@@ -501,6 +501,9 @@ internal static string MainCorruptedRegistry {
internal static string AllModVersionsInstallPrompt {
get { return (string)(ResourceManager.GetObject("AllModVersionsInstallPrompt", resourceCulture)); }
}
+ internal static string ModpackInstallIncompatiblePrompt {
+ get { return (string)(ResourceManager.GetObject("ModpackInstallIncompatiblePrompt", resourceCulture)); }
+ }
internal static string AllModVersionsInstallYes {
get { return (string)(ResourceManager.GetObject("AllModVersionsInstallYes", resourceCulture)); }
}
diff --git a/GUI/Properties/Resources.resx b/GUI/Properties/Resources.resx
index ea72d623ae..31e28d0460 100644
--- a/GUI/Properties/Resources.resx
+++ b/GUI/Properties/Resources.resx
@@ -214,9 +214,14 @@ Try to move {2} out of {3} and restart CKAN.
Corrupted registry archived to {0}: {1}
This means that CKAN forgot about all your installed mods, but they are still in GameData. You can reinstall them by importing the {2} file.
- {0} is not supported on your current game version and may not work at all. If you have any problems with it, you should NOT ask its maintainers for help.
+ {0} is not supported on your current compatible game versions ({1}) and may not work at all. If you have any problems with it, you should NOT ask its maintainers for help.
Do you really want to install it?
+ Some of the selected mods (or their dependencies) are not supported on your current compatible game versions ({1}) and may not work at all. If you have any problems with them, you should NOT ask their maintainers for help.
+
+{0}
+
+Are you sure you want to install them? Cancelling will abort the entire installation.
Install
Cancel
Update selected by user to version {0}.
diff --git a/Tests/Core/Versioning/KspVersionRangeTests.cs b/Tests/Core/Versioning/KspVersionRangeTests.cs
index 57d7757cd9..538675ecf0 100644
--- a/Tests/Core/Versioning/KspVersionRangeTests.cs
+++ b/Tests/Core/Versioning/KspVersionRangeTests.cs
@@ -628,7 +628,7 @@ public void VersionSpan_AllVersions_CorrectString()
// Act
string s = GameVersionRange.VersionSpan(game, min, max);
// Assert
- Assert.AreEqual("KSP all versions", s);
+ Assert.AreEqual("KSP All versions", s);
}
[Test]