Skip to content

Commit

Permalink
Merge pull request #264 from pjf/36_conflicts
Browse files Browse the repository at this point in the history
Our relationship resolver no longer tries to install conflicting mods.
  • Loading branch information
techman83 committed Nov 5, 2014
2 parents 1cdccf7 + e6f9e0c commit 4bb1ef3
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 2 deletions.
30 changes: 30 additions & 0 deletions CKAN/CKAN/Module.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema;
using System.Linq;

namespace CKAN
{
Expand Down Expand Up @@ -219,6 +220,35 @@ private void DeSerialisationFixes(StreamingContext like_i_could_care)
}
}

/// <summary>
/// Returns true if we conflict with the given module.
/// </summary>
public bool ConflictsWith(Module module)
{
return UniConflicts(this, module) || UniConflicts(module, this);
}

/// <summary>
/// Checks if A conflicts with B, but not if B conflicts with A.
/// Used by ConflictsWith.
/// </summary>
internal static bool UniConflicts(Module mod1, Module mod2)
{
if (mod1.conflicts == null)
{
return false;
}

foreach (RelationshipDescriptor conflict in mod1.conflicts)
{
if (mod2.ProvidesList.Contains(conflict.name))
{
return true;
}
}
return false;
}

/// <summary>
/// Returns true if our mod is compatible with the KSP version specified.
/// </summary>
Expand Down
55 changes: 53 additions & 2 deletions CKAN/CKAN/RelationshipResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ public struct RelationshipResolverOptions
// Alas, it appears that structs cannot have defaults. Try
// DefaultOpts() to get friendly defaults.

// TODO: RR currently conducts a depth-first resolution of requirements. While we do the
// right thing in processing all depdenencies first, then recommends, and then suggests,
// we could find that a recommendation many layers deep prevents a recommendation in the
// original mod's recommends list.
//
// If we resolved in things breadth-first order, we're less likely to encounter surprises
// where a nth-deep recommend blocks a top-level recommend.

public class RelationshipResolver
{
// A list of all the mods we're going to install.
Expand Down Expand Up @@ -48,6 +56,15 @@ public RelationshipResolver(List<string> modules, RelationshipResolverOptions op
}

log.DebugFormat("Preparing to resolve relationships for {0} {1}", mod.identifier, mod.version);

foreach (CkanModule listed_mod in this.modlist.Values)
{
if (listed_mod.ConflictsWith(mod))
{
throw new InconsistentKraken(string.Format("{0} conflicts with {1}, can't install both.",mod, listed_mod));
}
}

user_requested_mods.Add(mod);
this.Add(mod);
}
Expand Down Expand Up @@ -159,8 +176,42 @@ private void ResolveStanza(List<RelationshipDescriptor> stanza, RelationshipReso
throw new TooManyModsProvideKraken(dep_name, candidates);
}

Add(candidates[0]);
Resolve(candidates[0], options);
CkanModule candidate = candidates[0];

foreach (CkanModule mod in this.modlist.Values)
{
if (mod.ConflictsWith(candidate))
{
if (soft_resolve)
{
log.InfoFormat("{0} would cause conflicts, excluding it from consideration", candidate);

// I want labeled loops please, so I don't have to set this to null,
// break, and then look at it at the end. o_O
candidate = null;
break;
}
else
{
var this_is_why_we_cant_have_nice_things = new List<string> {
string.Format(
"{0} and {1} conflict with each other, yet we require them both!",
candidate, mod)
};

throw new InconsistentKraken(this_is_why_we_cant_have_nice_things);
}
}
}

// Our candidate may have been set to null if it was vetoed by our
// sanity check above.
if (candidate != null)
{
// Okay, looks like we want this one. Adding.
Add(candidate);
Resolve(candidate, options);
}
}
}

Expand Down
6 changes: 6 additions & 0 deletions CKAN/CKAN/Types/Kraken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,12 @@ public InconsistentKraken(List<string> inconsistencies, Exception inner_exceptio
this.inconsistencies = inconsistencies;
}

public InconsistentKraken(string inconsistency, Exception inner_exception = null)
:base(null, inner_exception)
{
this.inconsistencies = new List<string> { inconsistency };
}

public override string ToString()
{
return this.InconsistenciesPretty + this.StackTrace;
Expand Down
6 changes: 6 additions & 0 deletions CKAN/CmdLine/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,12 @@ private static int Install(InstallOptions options)
User.WriteLine("Your GameData has been returned to its original state.");
return Exit.ERROR;
}
catch (InconsistentKraken ex)
{
// The prettiest Kraken formats itself for us.
User.WriteLine(ex.InconsistenciesPretty);
return Exit.ERROR;
}

User.WriteLine("\nDone!\n");

Expand Down

0 comments on commit 4bb1ef3

Please sign in to comment.