From 5b9e365b5d252e2d1e957303c6b06fa1b3ae2f14 Mon Sep 17 00:00:00 2001
From: Dan Volchek <volchek2@illinois.edu>
Date: Sun, 10 Jun 2018 13:15:53 -0700
Subject: add version info to ModMetadata, change update checking to consider
 preview versions

---
 src/SMAPI/Framework/IModMetadata.cs           | 17 +++++++++++++++++
 src/SMAPI/Framework/ModLoading/ModMetadata.cs | 27 +++++++++++++++++++++++++++
 src/SMAPI/Program.cs                          | 23 ++++++++++++++++-------
 3 files changed, 60 insertions(+), 7 deletions(-)

(limited to 'src/SMAPI')

diff --git a/src/SMAPI/Framework/IModMetadata.cs b/src/SMAPI/Framework/IModMetadata.cs
index c0d6408d..673c5b2e 100644
--- a/src/SMAPI/Framework/IModMetadata.cs
+++ b/src/SMAPI/Framework/IModMetadata.cs
@@ -45,6 +45,14 @@ namespace StardewModdingAPI.Framework
         /// <summary>Whether the mod is a content pack.</summary>
         bool IsContentPack { get; }
 
+        /// <summary>The latest version of the mod.</summary>
+        ISemanticVersion LatestVersion { get; }
+
+        /// <summary>The latest preview version of the mod, if any.</summary>
+        ISemanticVersion LatestPreviewVersion { get; }
+
+        /// <summary>The error checking for updates for this mod, if any.</summary>
+        string UpdateCheckError { get; }
 
         /*********
         ** Public methods
@@ -72,6 +80,15 @@ namespace StardewModdingAPI.Framework
         /// <param name="api">The mod-provided API.</param>
         IModMetadata SetApi(object api);
 
+        /// <summary>Set the update status, indicating no errors happened.</summary>
+        /// <param name="latestVersion">The latest version.</param>
+        /// <param name="latestPreviewVersion">The latest preview version.</param>
+        IModMetadata SetUpdateStatus(ISemanticVersion latestVersion, ISemanticVersion latestPreviewVersion);
+
+        /// <summary>Set the update status, indicating an error happened.</summary>
+        /// <param name="updateCheckError">The error checking for updates, if any.</param>
+        IModMetadata SetUpdateStatus(string updateCheckError);
+
         /// <summary>Whether the mod manifest was loaded (regardless of whether the mod itself was loaded).</summary>
         bool HasManifest();
 
diff --git a/src/SMAPI/Framework/ModLoading/ModMetadata.cs b/src/SMAPI/Framework/ModLoading/ModMetadata.cs
index e4ec7e3b..8f544da3 100644
--- a/src/SMAPI/Framework/ModLoading/ModMetadata.cs
+++ b/src/SMAPI/Framework/ModLoading/ModMetadata.cs
@@ -43,6 +43,15 @@ namespace StardewModdingAPI.Framework.ModLoading
         /// <summary>The mod-provided API (if any).</summary>
         public object Api { get; private set; }
 
+        /// <summary>The latest version of the mod.</summary>
+        public ISemanticVersion LatestVersion { get; private set; }
+
+        /// <summary>The latest preview version of the mod, if any.</summary>
+        public ISemanticVersion LatestPreviewVersion { get; private set; }
+
+        /// <summary>The error checking for updates for this mod, if any.</summary>
+        public string UpdateCheckError { get; private set; }
+
         /// <summary>Whether the mod is a content pack.</summary>
         public bool IsContentPack => this.Manifest?.ContentPackFor != null;
 
@@ -115,6 +124,24 @@ namespace StardewModdingAPI.Framework.ModLoading
             return this;
         }
 
+        /// <summary>Set the update status.</summary>
+        /// <param name="latestVersion">The latest version.</param>
+        /// <param name="latestPreviewVersion">The latest preview version.</param>
+        public IModMetadata SetUpdateStatus(ISemanticVersion latestVersion, ISemanticVersion latestPreviewVersion)
+        {
+            this.LatestVersion = latestVersion;
+            this.LatestPreviewVersion = latestPreviewVersion;
+            return this;
+        }
+
+        // <summary>Set the update status, indicating an error happened.</summary>
+        /// <param name="updateCheckError">The error checking for updates, if any.</param>
+        public IModMetadata SetUpdateStatus(string updateCheckError)
+        {
+            this.UpdateCheckError = updateCheckError;
+            return this;
+        }
+
         /// <summary>Whether the mod manifest was loaded (regardless of whether the mod itself was loaded).</summary>
         public bool HasManifest()
         {
diff --git a/src/SMAPI/Program.cs b/src/SMAPI/Program.cs
index 8c6cea1e..dc034331 100644
--- a/src/SMAPI/Program.cs
+++ b/src/SMAPI/Program.cs
@@ -662,7 +662,7 @@ namespace StardewModdingAPI
                             .ToArray();
 
                         // extract latest versions
-                        IDictionary<IModMetadata, ModInfoModel> updatesByMod = new Dictionary<IModMetadata, ModInfoModel>();
+                        IDictionary<IModMetadata, Tuple<ModInfoModel, bool>> updatesByMod = new Dictionary<IModMetadata, Tuple<ModInfoModel, bool>>();
                         foreach (var result in results)
                         {
                             IModMetadata mod = result.Mod;
@@ -671,6 +671,7 @@ namespace StardewModdingAPI
                             // handle error
                             if (remoteInfo.Error != null)
                             {
+                                mod.SetUpdateStatus(remoteInfo.Error);
                                 this.Monitor.Log($"   {mod.DisplayName} ({result.Key}): update error: {remoteInfo.Error}", LogLevel.Trace);
                                 continue;
                             }
@@ -679,17 +680,25 @@ namespace StardewModdingAPI
                             ISemanticVersion localVersion = mod.DataRecord?.GetLocalVersionForUpdateChecks(mod.Manifest.Version) ?? mod.Manifest.Version;
                             if (!SemanticVersion.TryParse(mod.DataRecord?.GetRemoteVersionForUpdateChecks(remoteInfo.Version) ?? remoteInfo.Version, out ISemanticVersion remoteVersion))
                             {
-                                this.Monitor.Log($"   {mod.DisplayName} ({result.Key}): update error: Mod has invalid version {remoteInfo.Version}", LogLevel.Trace);
+                                string errorInfo = $"Mod has invalid version {remoteInfo.Version}";
+
+                                mod.SetUpdateStatus(errorInfo);
+                                this.Monitor.Log($"   {mod.DisplayName} ({result.Key}): update error: {errorInfo}", LogLevel.Trace);
                                 continue;
                             }
 
+                            SemanticVersion.TryParse(remoteInfo.PreviewVersion, out ISemanticVersion remotePreviewVersion);
+                            mod.SetUpdateStatus(remoteVersion, remotePreviewVersion);
+
                             // compare versions
-                            bool isUpdate = remoteVersion.IsNewerThan(localVersion);
-                            this.VerboseLog($"   {mod.DisplayName} ({result.Key}): {(isUpdate ? $"{mod.Manifest.Version}{(!localVersion.Equals(mod.Manifest.Version) ? $" [{localVersion}]" : "")} => {remoteInfo.Version}" : "okay")}.");
+                            bool isNonPreviewUpdate = remoteVersion.IsNewerThan(localVersion);
+                            bool isUpdate = isNonPreviewUpdate ||
+                                            (localVersion.IsNewerThan(remoteVersion) && remotePreviewVersion.IsNewerThan(localVersion));
+                            this.VerboseLog($"   {mod.DisplayName} ({result.Key}): {(isUpdate ? $"{mod.Manifest.Version}{(!localVersion.Equals(mod.Manifest.Version) ? $" [{localVersion}]" : "")} => {(isNonPreviewUpdate ? remoteInfo.Version : remoteInfo.PreviewVersion)}" : "okay")}.");
                             if (isUpdate)
                             {
-                                if (!updatesByMod.TryGetValue(mod, out ModInfoModel other) || remoteVersion.IsNewerThan(other.Version))
-                                    updatesByMod[mod] = remoteInfo;
+                                if (!updatesByMod.TryGetValue(mod, out Tuple<ModInfoModel, bool> other) || (isNonPreviewUpdate ? remoteVersion : remotePreviewVersion).IsNewerThan(other.Item2 ? other.Item1.PreviewVersion : other.Item1.Version))
+                                    updatesByMod[mod] = new Tuple<ModInfoModel, bool>(remoteInfo, !isNonPreviewUpdate);
                             }
                         }
 
@@ -699,7 +708,7 @@ namespace StardewModdingAPI
                             this.Monitor.Newline();
                             this.Monitor.Log($"You can update {updatesByMod.Count} mod{(updatesByMod.Count != 1 ? "s" : "")}:", LogLevel.Alert);
                             foreach (var entry in updatesByMod.OrderBy(p => p.Key.DisplayName))
-                                this.Monitor.Log($"   {entry.Key.DisplayName} {entry.Value.Version}: {entry.Value.Url}", LogLevel.Alert);
+                                this.Monitor.Log($"   {entry.Key.DisplayName} {(entry.Value.Item2 ? entry.Value.Item1.PreviewVersion : entry.Value.Item1.Version)}: {entry.Value.Item1.Url}", LogLevel.Alert);
                         }
                         else
                             this.Monitor.Log("   All mods up to date.", LogLevel.Trace);
-- 
cgit 


From 1fa2632289134c39f268c374bb290549f28751d5 Mon Sep 17 00:00:00 2001
From: Dan Volchek <volchek2@illinois.edu>
Date: Sun, 10 Jun 2018 14:19:05 -0700
Subject: seperate IModMetadata methods, call them correctly when updating

---
 src/SMAPI/Framework/IModMetadata.cs           | 13 ++++++-----
 src/SMAPI/Framework/ModLoading/ModMetadata.cs | 18 ++++++++++------
 src/SMAPI/Program.cs                          | 31 +++++++++++++++++++--------
 3 files changed, 42 insertions(+), 20 deletions(-)

(limited to 'src/SMAPI')

diff --git a/src/SMAPI/Framework/IModMetadata.cs b/src/SMAPI/Framework/IModMetadata.cs
index 673c5b2e..7bf7d98c 100644
--- a/src/SMAPI/Framework/IModMetadata.cs
+++ b/src/SMAPI/Framework/IModMetadata.cs
@@ -80,14 +80,17 @@ namespace StardewModdingAPI.Framework
         /// <param name="api">The mod-provided API.</param>
         IModMetadata SetApi(object api);
 
-        /// <summary>Set the update status, indicating no errors happened.</summary>
+        /// <summary>Set the update version.</summary>
         /// <param name="latestVersion">The latest version.</param>
+        IModMetadata SetUpdateVersion(ISemanticVersion latestVersion);
+
+        /// <summary>Set the preview update version.</summary>
         /// <param name="latestPreviewVersion">The latest preview version.</param>
-        IModMetadata SetUpdateStatus(ISemanticVersion latestVersion, ISemanticVersion latestPreviewVersion);
+        IModMetadata SetPreviewUpdateVersion(ISemanticVersion latestPreviewVersion);
 
-        /// <summary>Set the update status, indicating an error happened.</summary>
-        /// <param name="updateCheckError">The error checking for updates, if any.</param>
-        IModMetadata SetUpdateStatus(string updateCheckError);
+        /// <summary>Set the error that occured while checking for updates.</summary>
+        /// <param name="updateCheckError">The error checking for updates.</param>
+        IModMetadata SetUpdateError(string updateCheckError);
 
         /// <summary>Whether the mod manifest was loaded (regardless of whether the mod itself was loaded).</summary>
         bool HasManifest();
diff --git a/src/SMAPI/Framework/ModLoading/ModMetadata.cs b/src/SMAPI/Framework/ModLoading/ModMetadata.cs
index 8f544da3..1ead1387 100644
--- a/src/SMAPI/Framework/ModLoading/ModMetadata.cs
+++ b/src/SMAPI/Framework/ModLoading/ModMetadata.cs
@@ -124,19 +124,25 @@ namespace StardewModdingAPI.Framework.ModLoading
             return this;
         }
 
-        /// <summary>Set the update status.</summary>
+        /// <summary>Set the update version.</summary>
         /// <param name="latestVersion">The latest version.</param>
-        /// <param name="latestPreviewVersion">The latest preview version.</param>
-        public IModMetadata SetUpdateStatus(ISemanticVersion latestVersion, ISemanticVersion latestPreviewVersion)
+        public IModMetadata SetUpdateVersion(ISemanticVersion latestVersion)
         {
             this.LatestVersion = latestVersion;
+            return this;
+        }
+
+        /// <summary>Set the preview update version.</summary>
+        /// <param name="latestPreviewVersion">The latest preview version.</param>
+        public IModMetadata SetPreviewUpdateVersion(ISemanticVersion latestPreviewVersion)
+        {
             this.LatestPreviewVersion = latestPreviewVersion;
             return this;
         }
 
-        // <summary>Set the update status, indicating an error happened.</summary>
-        /// <param name="updateCheckError">The error checking for updates, if any.</param>
-        public IModMetadata SetUpdateStatus(string updateCheckError)
+        /// <summary>Set the error that occured while checking for updates.</summary>
+        /// <param name="updateCheckError">The error checking for updates.</param>
+        public IModMetadata SetUpdateError(string updateCheckError)
         {
             this.UpdateCheckError = updateCheckError;
             return this;
diff --git a/src/SMAPI/Program.cs b/src/SMAPI/Program.cs
index dc034331..e6aafb2d 100644
--- a/src/SMAPI/Program.cs
+++ b/src/SMAPI/Program.cs
@@ -671,34 +671,47 @@ namespace StardewModdingAPI
                             // handle error
                             if (remoteInfo.Error != null)
                             {
-                                mod.SetUpdateStatus(remoteInfo.Error);
+                                if(mod.LatestVersion == null && mod.LatestPreviewVersion == null)
+                                    mod.SetUpdateError(remoteInfo.Error);
                                 this.Monitor.Log($"   {mod.DisplayName} ({result.Key}): update error: {remoteInfo.Error}", LogLevel.Trace);
                                 continue;
                             }
 
                             // normalise versions
                             ISemanticVersion localVersion = mod.DataRecord?.GetLocalVersionForUpdateChecks(mod.Manifest.Version) ?? mod.Manifest.Version;
-                            if (!SemanticVersion.TryParse(mod.DataRecord?.GetRemoteVersionForUpdateChecks(remoteInfo.Version) ?? remoteInfo.Version, out ISemanticVersion remoteVersion))
+
+                            bool validVersion = SemanticVersion.TryParse(mod.DataRecord?.GetRemoteVersionForUpdateChecks(remoteInfo.Version) ?? remoteInfo.Version, out ISemanticVersion remoteVersion);
+                            bool validPreviewVersion = SemanticVersion.TryParse(remoteInfo.PreviewVersion, out ISemanticVersion remotePreviewVersion);
+
+                            if (!validVersion && !validPreviewVersion)
                             {
-                                string errorInfo = $"Mod has invalid version {remoteInfo.Version}";
+                                string errorInfo = $"Mod has invalid versions. version: {remoteInfo.Version}, preview version: {remoteInfo.PreviewVersion}";
 
-                                mod.SetUpdateStatus(errorInfo);
+                                if (mod.LatestVersion == null && mod.LatestPreviewVersion == null)
+                                    mod.SetUpdateError(errorInfo);
                                 this.Monitor.Log($"   {mod.DisplayName} ({result.Key}): update error: {errorInfo}", LogLevel.Trace);
                                 continue;
                             }
 
-                            SemanticVersion.TryParse(remoteInfo.PreviewVersion, out ISemanticVersion remotePreviewVersion);
-                            mod.SetUpdateStatus(remoteVersion, remotePreviewVersion);
-
                             // compare versions
-                            bool isNonPreviewUpdate = remoteVersion.IsNewerThan(localVersion);
+                            bool isNonPreviewUpdate = validVersion && remoteVersion.IsNewerThan(localVersion);
+
                             bool isUpdate = isNonPreviewUpdate ||
-                                            (localVersion.IsNewerThan(remoteVersion) && remotePreviewVersion.IsNewerThan(localVersion));
+                                            (validPreviewVersion && localVersion.IsNewerThan(remoteVersion) && remotePreviewVersion.IsNewerThan(localVersion));
                             this.VerboseLog($"   {mod.DisplayName} ({result.Key}): {(isUpdate ? $"{mod.Manifest.Version}{(!localVersion.Equals(mod.Manifest.Version) ? $" [{localVersion}]" : "")} => {(isNonPreviewUpdate ? remoteInfo.Version : remoteInfo.PreviewVersion)}" : "okay")}.");
                             if (isUpdate)
                             {
                                 if (!updatesByMod.TryGetValue(mod, out Tuple<ModInfoModel, bool> other) || (isNonPreviewUpdate ? remoteVersion : remotePreviewVersion).IsNewerThan(other.Item2 ? other.Item1.PreviewVersion : other.Item1.Version))
+                                {
                                     updatesByMod[mod] = new Tuple<ModInfoModel, bool>(remoteInfo, !isNonPreviewUpdate);
+
+                                    if (isNonPreviewUpdate)
+                                        mod.SetUpdateVersion(remoteVersion);
+                                    else
+                                        mod.SetPreviewUpdateVersion(remotePreviewVersion);
+
+                                    mod.SetUpdateError(null);
+                                }
                             }
                         }
 
-- 
cgit 


From 0b2e46d55cb09a169c7bb64ade37c82fc8233cb3 Mon Sep 17 00:00:00 2001
From: Dan Volchek <volchek2@illinois.edu>
Date: Sun, 10 Jun 2018 15:05:59 -0700
Subject: refactor IModMetadata update info

---
 src/SMAPI/Framework/IModMetadata.cs                | 28 ++++++----------
 src/SMAPI/Framework/ModLoading/ModMetadata.cs      | 36 ++++++++------------
 .../Framework/ModUpdateChecking/ModUpdateStatus.cs | 32 ++++++++++++++++++
 src/SMAPI/Program.cs                               | 39 +++++++++++-----------
 src/SMAPI/StardewModdingAPI.csproj                 |  1 +
 5 files changed, 77 insertions(+), 59 deletions(-)
 create mode 100644 src/SMAPI/Framework/ModUpdateChecking/ModUpdateStatus.cs

(limited to 'src/SMAPI')

diff --git a/src/SMAPI/Framework/IModMetadata.cs b/src/SMAPI/Framework/IModMetadata.cs
index 7bf7d98c..b71c8056 100644
--- a/src/SMAPI/Framework/IModMetadata.cs
+++ b/src/SMAPI/Framework/IModMetadata.cs
@@ -1,5 +1,6 @@
 using StardewModdingAPI.Framework.ModData;
 using StardewModdingAPI.Framework.ModLoading;
+using StardewModdingAPI.Framework.ModUpdateChecking;
 
 namespace StardewModdingAPI.Framework
 {
@@ -45,14 +46,11 @@ namespace StardewModdingAPI.Framework
         /// <summary>Whether the mod is a content pack.</summary>
         bool IsContentPack { get; }
 
-        /// <summary>The latest version of the mod.</summary>
-        ISemanticVersion LatestVersion { get; }
+        /// <summary>The update status of this mod (if any).</summary>
+        ModUpdateStatus UpdateStatus { get; }
 
-        /// <summary>The latest preview version of the mod, if any.</summary>
-        ISemanticVersion LatestPreviewVersion { get; }
-
-        /// <summary>The error checking for updates for this mod, if any.</summary>
-        string UpdateCheckError { get; }
+        /// <summary>The preview update status of this mod (if any).</summary>
+        ModUpdateStatus PreviewUpdateStatus { get; }
 
         /*********
         ** Public methods
@@ -80,17 +78,13 @@ namespace StardewModdingAPI.Framework
         /// <param name="api">The mod-provided API.</param>
         IModMetadata SetApi(object api);
 
-        /// <summary>Set the update version.</summary>
-        /// <param name="latestVersion">The latest version.</param>
-        IModMetadata SetUpdateVersion(ISemanticVersion latestVersion);
-
-        /// <summary>Set the preview update version.</summary>
-        /// <param name="latestPreviewVersion">The latest preview version.</param>
-        IModMetadata SetPreviewUpdateVersion(ISemanticVersion latestPreviewVersion);
+        /// <summary>Set the update status.</summary>
+        /// <param name="updateStatus">The mod update status.</param>
+        IModMetadata SetUpdateStatus(ModUpdateStatus updateStatus);
 
-        /// <summary>Set the error that occured while checking for updates.</summary>
-        /// <param name="updateCheckError">The error checking for updates.</param>
-        IModMetadata SetUpdateError(string updateCheckError);
+        /// <summary>Set the preview update status.</summary>
+        /// <param name="previewUpdateStatus">The mod preview update status.</param>
+        IModMetadata SetPreviewUpdateStatus(ModUpdateStatus previewUpdateStatus);
 
         /// <summary>Whether the mod manifest was loaded (regardless of whether the mod itself was loaded).</summary>
         bool HasManifest();
diff --git a/src/SMAPI/Framework/ModLoading/ModMetadata.cs b/src/SMAPI/Framework/ModLoading/ModMetadata.cs
index 1ead1387..88d2770c 100644
--- a/src/SMAPI/Framework/ModLoading/ModMetadata.cs
+++ b/src/SMAPI/Framework/ModLoading/ModMetadata.cs
@@ -1,6 +1,7 @@
 using System;
 using System.Linq;
 using StardewModdingAPI.Framework.ModData;
+using StardewModdingAPI.Framework.ModUpdateChecking;
 
 namespace StardewModdingAPI.Framework.ModLoading
 {
@@ -43,14 +44,11 @@ namespace StardewModdingAPI.Framework.ModLoading
         /// <summary>The mod-provided API (if any).</summary>
         public object Api { get; private set; }
 
-        /// <summary>The latest version of the mod.</summary>
-        public ISemanticVersion LatestVersion { get; private set; }
+        /// <summary>The update status of this mod (if any).</summary>
+        public ModUpdateStatus UpdateStatus { get; private set; }
 
-        /// <summary>The latest preview version of the mod, if any.</summary>
-        public ISemanticVersion LatestPreviewVersion { get; private set; }
-
-        /// <summary>The error checking for updates for this mod, if any.</summary>
-        public string UpdateCheckError { get; private set; }
+        /// <summary>The preview update status of this mod (if any).</summary>
+        public ModUpdateStatus PreviewUpdateStatus { get; private set; }
 
         /// <summary>Whether the mod is a content pack.</summary>
         public bool IsContentPack => this.Manifest?.ContentPackFor != null;
@@ -124,27 +122,19 @@ namespace StardewModdingAPI.Framework.ModLoading
             return this;
         }
 
-        /// <summary>Set the update version.</summary>
-        /// <param name="latestVersion">The latest version.</param>
-        public IModMetadata SetUpdateVersion(ISemanticVersion latestVersion)
-        {
-            this.LatestVersion = latestVersion;
-            return this;
-        }
-
-        /// <summary>Set the preview update version.</summary>
-        /// <param name="latestPreviewVersion">The latest preview version.</param>
-        public IModMetadata SetPreviewUpdateVersion(ISemanticVersion latestPreviewVersion)
+        /// <summary>Set the update status.</summary>
+        /// <param name="updateStatus">The mod update status.</param>
+        public IModMetadata SetUpdateStatus(ModUpdateStatus updateStatus)
         {
-            this.LatestPreviewVersion = latestPreviewVersion;
+            this.UpdateStatus = updateStatus;
             return this;
         }
 
-        /// <summary>Set the error that occured while checking for updates.</summary>
-        /// <param name="updateCheckError">The error checking for updates.</param>
-        public IModMetadata SetUpdateError(string updateCheckError)
+        /// <summary>Set the preview update status.</summary>
+        /// <param name="previewUpdateStatus">The mod preview update status.</param>
+        public IModMetadata SetPreviewUpdateStatus(ModUpdateStatus previewUpdateStatus)
         {
-            this.UpdateCheckError = updateCheckError;
+            this.PreviewUpdateStatus = previewUpdateStatus;
             return this;
         }
 
diff --git a/src/SMAPI/Framework/ModUpdateChecking/ModUpdateStatus.cs b/src/SMAPI/Framework/ModUpdateChecking/ModUpdateStatus.cs
new file mode 100644
index 00000000..7f588b66
--- /dev/null
+++ b/src/SMAPI/Framework/ModUpdateChecking/ModUpdateStatus.cs
@@ -0,0 +1,32 @@
+namespace StardewModdingAPI.Framework.ModUpdateChecking
+{
+    /// <summary>Update status for a mod.</summary>
+    internal class ModUpdateStatus
+    {
+        /*********
+        ** Accessors
+        *********/
+        /// <summary>The version that this mod can be updated to (if any).</summary>
+        public ISemanticVersion Version { get; }
+
+        /// <summary>The error checking for updates of this mod (if any).</summary>
+        public string Error { get; }
+
+        /*********
+        ** Public methods
+        *********/
+        /// <summary>Construct an instance.</summary>
+        /// <param name="version">The version that this mod can be update to.</param>
+        public ModUpdateStatus(ISemanticVersion version)
+        {
+            this.Version = version;
+        }
+
+        /// <summary>Construct an instance.</summary>
+        /// <param name="error">The error checking for updates of this mod.</param>
+        public ModUpdateStatus(string error)
+        {
+            this.Error = error;
+        }
+    }
+}
diff --git a/src/SMAPI/Program.cs b/src/SMAPI/Program.cs
index e6aafb2d..96061b2a 100644
--- a/src/SMAPI/Program.cs
+++ b/src/SMAPI/Program.cs
@@ -24,6 +24,7 @@ using StardewModdingAPI.Framework.ModData;
 using StardewModdingAPI.Framework.Models;
 using StardewModdingAPI.Framework.ModHelpers;
 using StardewModdingAPI.Framework.ModLoading;
+using StardewModdingAPI.Framework.ModUpdateChecking;
 using StardewModdingAPI.Framework.Patching;
 using StardewModdingAPI.Framework.Reflection;
 using StardewModdingAPI.Framework.Serialisation;
@@ -671,46 +672,46 @@ namespace StardewModdingAPI
                             // handle error
                             if (remoteInfo.Error != null)
                             {
-                                if(mod.LatestVersion == null && mod.LatestPreviewVersion == null)
-                                    mod.SetUpdateError(remoteInfo.Error);
+                                if (mod.UpdateStatus?.Version == null)
+                                    mod.SetUpdateStatus(new ModUpdateStatus(remoteInfo.Error));
+                                if (mod.PreviewUpdateStatus?.Version == null)
+                                    mod.SetUpdateStatus(new ModUpdateStatus(remoteInfo.Error));
+
                                 this.Monitor.Log($"   {mod.DisplayName} ({result.Key}): update error: {remoteInfo.Error}", LogLevel.Trace);
                                 continue;
                             }
 
                             // normalise versions
                             ISemanticVersion localVersion = mod.DataRecord?.GetLocalVersionForUpdateChecks(mod.Manifest.Version) ?? mod.Manifest.Version;
-
                             bool validVersion = SemanticVersion.TryParse(mod.DataRecord?.GetRemoteVersionForUpdateChecks(remoteInfo.Version) ?? remoteInfo.Version, out ISemanticVersion remoteVersion);
                             bool validPreviewVersion = SemanticVersion.TryParse(remoteInfo.PreviewVersion, out ISemanticVersion remotePreviewVersion);
 
+                            if (!validVersion && mod.UpdateStatus?.Version == null)
+                                mod.SetUpdateStatus(new ModUpdateStatus($"Version is invalid: {remoteInfo.Version}"));
+                            if (!validPreviewVersion && mod.PreviewUpdateStatus?.Version == null)
+                                mod.SetPreviewUpdateStatus(new ModUpdateStatus($"Version is invalid: {remoteInfo.PreviewVersion}"));
+
                             if (!validVersion && !validPreviewVersion)
                             {
-                                string errorInfo = $"Mod has invalid versions. version: {remoteInfo.Version}, preview version: {remoteInfo.PreviewVersion}";
-
-                                if (mod.LatestVersion == null && mod.LatestPreviewVersion == null)
-                                    mod.SetUpdateError(errorInfo);
-                                this.Monitor.Log($"   {mod.DisplayName} ({result.Key}): update error: {errorInfo}", LogLevel.Trace);
+                                this.Monitor.Log($"   {mod.DisplayName} ({result.Key}): update error: Mod has invalid versions. version: {remoteInfo.Version}, preview version: {remoteInfo.PreviewVersion}", LogLevel.Trace);
                                 continue;
                             }
 
                             // compare versions
-                            bool isNonPreviewUpdate = validVersion && remoteVersion.IsNewerThan(localVersion);
+                            bool isPreviewUpdate = validPreviewVersion && localVersion.IsNewerThan(remoteVersion) && remotePreviewVersion.IsNewerThan(localVersion);
+                            bool isUpdate = (validVersion && remoteVersion.IsNewerThan(localVersion)) || isPreviewUpdate;
 
-                            bool isUpdate = isNonPreviewUpdate ||
-                                            (validPreviewVersion && localVersion.IsNewerThan(remoteVersion) && remotePreviewVersion.IsNewerThan(localVersion));
-                            this.VerboseLog($"   {mod.DisplayName} ({result.Key}): {(isUpdate ? $"{mod.Manifest.Version}{(!localVersion.Equals(mod.Manifest.Version) ? $" [{localVersion}]" : "")} => {(isNonPreviewUpdate ? remoteInfo.Version : remoteInfo.PreviewVersion)}" : "okay")}.");
+                            this.VerboseLog($"   {mod.DisplayName} ({result.Key}): {(isUpdate ? $"{mod.Manifest.Version}{(!localVersion.Equals(mod.Manifest.Version) ? $" [{localVersion}]" : "")} => {(isPreviewUpdate ? remoteInfo.PreviewVersion : remoteInfo.Version)}" : "okay")}.");
                             if (isUpdate)
                             {
-                                if (!updatesByMod.TryGetValue(mod, out Tuple<ModInfoModel, bool> other) || (isNonPreviewUpdate ? remoteVersion : remotePreviewVersion).IsNewerThan(other.Item2 ? other.Item1.PreviewVersion : other.Item1.Version))
+                                if (!updatesByMod.TryGetValue(mod, out Tuple<ModInfoModel, bool> other) || (isPreviewUpdate ? remotePreviewVersion : remoteVersion).IsNewerThan(other.Item2 ? other.Item1.PreviewVersion : other.Item1.Version))
                                 {
-                                    updatesByMod[mod] = new Tuple<ModInfoModel, bool>(remoteInfo, !isNonPreviewUpdate);
+                                    updatesByMod[mod] = new Tuple<ModInfoModel, bool>(remoteInfo, isPreviewUpdate);
 
-                                    if (isNonPreviewUpdate)
-                                        mod.SetUpdateVersion(remoteVersion);
+                                    if (isPreviewUpdate)
+                                        mod.SetPreviewUpdateStatus(new ModUpdateStatus(remotePreviewVersion));
                                     else
-                                        mod.SetPreviewUpdateVersion(remotePreviewVersion);
-
-                                    mod.SetUpdateError(null);
+                                        mod.SetUpdateStatus(new ModUpdateStatus(remoteVersion));
                                 }
                             }
                         }
diff --git a/src/SMAPI/StardewModdingAPI.csproj b/src/SMAPI/StardewModdingAPI.csproj
index 67c48a57..beaad8e0 100644
--- a/src/SMAPI/StardewModdingAPI.csproj
+++ b/src/SMAPI/StardewModdingAPI.csproj
@@ -108,6 +108,7 @@
     <Compile Include="Framework\ContentManagers\IContentManager.cs" />
     <Compile Include="Framework\ContentManagers\ModContentManager.cs" />
     <Compile Include="Framework\Models\ModFolderExport.cs" />
+    <Compile Include="Framework\ModUpdateChecking\ModUpdateStatus.cs" />
     <Compile Include="Framework\Patching\GamePatcher.cs" />
     <Compile Include="Framework\Patching\IHarmonyPatch.cs" />
     <Compile Include="Framework\Serialisation\ColorConverter.cs" />
-- 
cgit 


From 468eea751dff9c7c70f0eb33eb91adf98b79cf9d Mon Sep 17 00:00:00 2001
From: Dan Volchek <volchek2@illinois.edu>
Date: Sun, 10 Jun 2018 15:17:44 -0700
Subject: set mods without updates to have no updates

---
 src/SMAPI/Framework/ModUpdateChecking/ModUpdateStatus.cs | 5 +++++
 src/SMAPI/Program.cs                                     | 8 ++++++++
 2 files changed, 13 insertions(+)

(limited to 'src/SMAPI')

diff --git a/src/SMAPI/Framework/ModUpdateChecking/ModUpdateStatus.cs b/src/SMAPI/Framework/ModUpdateChecking/ModUpdateStatus.cs
index 7f588b66..efb32aef 100644
--- a/src/SMAPI/Framework/ModUpdateChecking/ModUpdateStatus.cs
+++ b/src/SMAPI/Framework/ModUpdateChecking/ModUpdateStatus.cs
@@ -28,5 +28,10 @@ namespace StardewModdingAPI.Framework.ModUpdateChecking
         {
             this.Error = error;
         }
+
+        /// <summary>Construct an instance.</summary>
+        public ModUpdateStatus()
+        {
+        }
     }
 }
diff --git a/src/SMAPI/Program.cs b/src/SMAPI/Program.cs
index 96061b2a..2ee18a29 100644
--- a/src/SMAPI/Program.cs
+++ b/src/SMAPI/Program.cs
@@ -716,6 +716,14 @@ namespace StardewModdingAPI
                             }
                         }
 
+                        // set mods to have no updates
+                        foreach (IModMetadata mod in results.Select(item => item.Mod)
+                            .Where(item => !updatesByMod.ContainsKey(item)))
+                        {
+                            mod.SetUpdateStatus(new ModUpdateStatus());
+                            mod.SetPreviewUpdateStatus(new ModUpdateStatus());
+                        }
+
                         // output
                         if (updatesByMod.Any())
                         {
-- 
cgit