move incompatible mod logic into mod registry

This commit is contained in:
Jesse Plamondon-Willard 2017-02-23 23:36:14 -05:00
parent 3005270437
commit 6b26eceb57
4 changed files with 73 additions and 36 deletions

View File

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text.RegularExpressions;
using StardewModdingAPI.Framework.Models;
namespace StardewModdingAPI.Framework namespace StardewModdingAPI.Framework
{ {
@ -18,10 +20,21 @@ namespace StardewModdingAPI.Framework
/// <summary>The friendly mod names treated as deprecation warning sources (assembly full name => mod name).</summary> /// <summary>The friendly mod names treated as deprecation warning sources (assembly full name => mod name).</summary>
private readonly IDictionary<string, string> ModNamesByAssembly = new Dictionary<string, string>(); private readonly IDictionary<string, string> ModNamesByAssembly = new Dictionary<string, string>();
/// <summary>The mod versions which should be disabled due to incompatibility.</summary>
private readonly IncompatibleMod[] IncompatibleMods;
/********* /*********
** Public methods ** Public methods
*********/ *********/
/// <summary>Construct an instance.</summary>
/// <param name="incompatibleMods">The mod versions which should be disabled due to incompatibility.</param>
public ModRegistry(IEnumerable<IncompatibleMod> incompatibleMods)
{
this.IncompatibleMods = incompatibleMods.ToArray();
}
/**** /****
** IModRegistry ** IModRegistry
****/ ****/
@ -113,5 +126,22 @@ namespace StardewModdingAPI.Framework
// no known assembly found // no known assembly found
return null; return null;
} }
/// <summary>Get a record indicating why a mod is incompatible (if applicable).</summary>
/// <param name="manifest">The mod manifest.</param>
/// <returns>Returns the incompatibility record if applicable, else <c>null</c>.</returns>
internal IncompatibleMod GetIncompatibilityRecord(IManifest manifest)
{
string key = !string.IsNullOrWhiteSpace(manifest.UniqueID) ? manifest.UniqueID : manifest.EntryDll;
return (
from mod in this.IncompatibleMods
where
mod.ID == key
&& (mod.LowerSemanticVersion == null || !manifest.Version.IsOlderThan(mod.LowerSemanticVersion))
&& !manifest.Version.IsNewerThan(mod.UpperSemanticVersion)
&& (string.IsNullOrWhiteSpace(mod.ForceCompatibleVersion) || !Regex.IsMatch(manifest.Version.ToString(), mod.ForceCompatibleVersion, RegexOptions.IgnoreCase))
select mod
).FirstOrDefault();
}
} }
} }

View File

@ -1,4 +1,5 @@
using System.Text.RegularExpressions; using System.Runtime.Serialization;
using Newtonsoft.Json;
namespace StardewModdingAPI.Framework.Models namespace StardewModdingAPI.Framework.Models
{ {
@ -8,6 +9,9 @@ namespace StardewModdingAPI.Framework.Models
/********* /*********
** Accessors ** Accessors
*********/ *********/
/****
** From config
****/
/// <summary>The unique mod ID.</summary> /// <summary>The unique mod ID.</summary>
public string ID { get; set; } public string ID { get; set; }
@ -34,24 +38,28 @@ namespace StardewModdingAPI.Framework.Models
public string ReasonPhrase { get; set; } public string ReasonPhrase { get; set; }
/****
** Injected
****/
/// <summary>The semantic version corresponding to <see cref="LowerVersion"/>.</summary>
[JsonIgnore]
public ISemanticVersion LowerSemanticVersion { get; set; }
/// <summary>The semantic version corresponding to <see cref="UpperVersion"/>.</summary>
[JsonIgnore]
public ISemanticVersion UpperSemanticVersion { get; set; }
/********* /*********
** Public methods ** Private methods
*********/ *********/
/// <summary>Get whether the specified version is compatible according to this metadata.</summary> /// <summary>The method called when the model finishes deserialising.</summary>
/// <param name="version">The current version of the matching mod.</param> /// <param name="context">The deserialisation context.</param>
public bool IsCompatible(ISemanticVersion version) [OnDeserialized]
private void OnDeserialized(StreamingContext context)
{ {
ISemanticVersion lowerVersion = this.LowerVersion != null ? new SemanticVersion(this.LowerVersion) : null; this.LowerSemanticVersion = this.LowerVersion != null ? new SemanticVersion(this.LowerVersion) : null;
ISemanticVersion upperVersion = new SemanticVersion(this.UpperVersion); this.UpperSemanticVersion = this.UpperVersion != null ? new SemanticVersion(this.UpperVersion) : null;
// ignore versions not in range
if (lowerVersion != null && version.IsOlderThan(lowerVersion))
return true;
if (version.IsNewerThan(upperVersion))
return true;
// allow versions matching override
return !string.IsNullOrWhiteSpace(this.ForceCompatibleVersion) && Regex.IsMatch(version.ToString(), this.ForceCompatibleVersion, RegexOptions.IgnoreCase);
} }
} }
} }

View File

@ -64,7 +64,7 @@ namespace StardewModdingAPI
internal SGame GameInstance; internal SGame GameInstance;
/// <summary>Tracks the installed mods.</summary> /// <summary>Tracks the installed mods.</summary>
internal readonly ModRegistry ModRegistry = new ModRegistry(); internal readonly ModRegistry ModRegistry;
/// <summary>Manages deprecation warnings.</summary> /// <summary>Manages deprecation warnings.</summary>
internal readonly DeprecationManager DeprecationManager; internal readonly DeprecationManager DeprecationManager;
@ -92,6 +92,7 @@ namespace StardewModdingAPI
// initialise // initialise
this.Monitor = new Monitor("SMAPI", this.ConsoleManager, this.LogFile, this.ExitGameImmediately) { WriteToConsole = writeToConsole }; this.Monitor = new Monitor("SMAPI", this.ConsoleManager, this.LogFile, this.ExitGameImmediately) { WriteToConsole = writeToConsole };
this.ModRegistry = new ModRegistry(this.Settings.IncompatibleMods);
this.DeprecationManager = new DeprecationManager(this.Monitor, this.ModRegistry); this.DeprecationManager = new DeprecationManager(this.Monitor, this.ModRegistry);
} }
@ -388,28 +389,22 @@ namespace StardewModdingAPI
continue; continue;
} }
// validate known incompatible mods // validate compatibility
IncompatibleMod compatibility = this.ModRegistry.GetIncompatibilityRecord(manifest);
if (compatibility != null)
{ {
string modKey = !string.IsNullOrWhiteSpace(manifest.UniqueID) ? manifest.UniqueID : manifest.EntryDll; bool hasOfficialUrl = !string.IsNullOrWhiteSpace(compatibility.UpdateUrl);
IncompatibleMod compatibility = this.Settings.IncompatibleMods.FirstOrDefault(p => p.ID == modKey); bool hasUnofficialUrl = !string.IsNullOrWhiteSpace(compatibility.UnofficialUpdateUrl);
if(compatibility != null)
{
if (!compatibility.IsCompatible(manifest.Version))
{
bool hasOfficialUrl = !string.IsNullOrWhiteSpace(compatibility.UpdateUrl);
bool hasUnofficialUrl = !string.IsNullOrWhiteSpace(compatibility.UnofficialUpdateUrl);
string reasonPhrase = compatibility.ReasonPhrase ?? "it isn't compatible with the latest version of the game"; string reasonPhrase = compatibility.ReasonPhrase ?? "it isn't compatible with the latest version of the game";
string warning = $"Skipped {compatibility.Name} because {reasonPhrase}. Please check for a version newer than {compatibility.UpperVersion} here:"; string warning = $"Skipped {compatibility.Name} because {reasonPhrase}. Please check for a version newer than {compatibility.UpperVersion} here:";
if (hasOfficialUrl) if (hasOfficialUrl)
warning += !hasUnofficialUrl ? $" {compatibility.UpdateUrl}" : $"{Environment.NewLine}- official mod: {compatibility.UpdateUrl}"; warning += !hasUnofficialUrl ? $" {compatibility.UpdateUrl}" : $"{Environment.NewLine}- official mod: {compatibility.UpdateUrl}";
if (hasUnofficialUrl) if (hasUnofficialUrl)
warning += $"{Environment.NewLine}- unofficial update: {compatibility.UnofficialUpdateUrl}"; warning += $"{Environment.NewLine}- unofficial update: {compatibility.UnofficialUpdateUrl}";
this.Monitor.Log(warning, LogLevel.Error); this.Monitor.Log(warning, LogLevel.Error);
continue; continue;
}
}
} }
// validate SMAPI version // validate SMAPI version

View File

@ -63,9 +63,13 @@ namespace StardewModdingAPI
/// <summary>Get an integer indicating whether this version precedes (less than 0), supercedes (more than 0), or is equivalent to (0) the specified version.</summary> /// <summary>Get an integer indicating whether this version precedes (less than 0), supercedes (more than 0), or is equivalent to (0) the specified version.</summary>
/// <param name="other">The version to compare with this instance.</param> /// <param name="other">The version to compare with this instance.</param>
/// <exception cref="ArgumentNullException">The <paramref name="other"/> value is null.</exception>
/// <remarks>The implementation is defined by Semantic Version 2.0 (http://semver.org/).</remarks> /// <remarks>The implementation is defined by Semantic Version 2.0 (http://semver.org/).</remarks>
public int CompareTo(ISemanticVersion other) public int CompareTo(ISemanticVersion other)
{ {
if(other == null)
throw new ArgumentNullException(nameof(other));
const int same = 0; const int same = 0;
const int curNewer = 1; const int curNewer = 1;
const int curOlder = -1; const int curOlder = -1;